From f6f691b63c1b2166ed005a462a6fba5001786eda Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Mon, 25 Sep 2017 19:23:07 -0700 Subject: [PATCH] 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 --- libbacktrace/UnwindStack.cpp | 150 +---- libunwindstack/Android.bp | 2 + libunwindstack/Regs.cpp | 20 - libunwindstack/Unwinder.cpp | 70 ++- libunwindstack/include/unwindstack/Regs.h | 4 - libunwindstack/include/unwindstack/Unwinder.h | 10 +- libunwindstack/tests/DwarfDebugFrameTest.cpp | 1 - libunwindstack/tests/DwarfEhFrameTest.cpp | 1 - libunwindstack/tests/DwarfOpTest.cpp | 6 +- libunwindstack/tests/DwarfSectionImplTest.cpp | 38 +- libunwindstack/tests/ElfFake.cpp | 65 ++ libunwindstack/tests/ElfFake.h | 84 +++ libunwindstack/tests/RegsFake.h | 48 +- libunwindstack/tests/RegsTest.cpp | 96 +-- libunwindstack/tests/UnwinderTest.cpp | 576 ++++++++++++++++++ 15 files changed, 894 insertions(+), 277 deletions(-) create mode 100644 libunwindstack/tests/ElfFake.cpp create mode 100644 libunwindstack/tests/ElfFake.h create mode 100644 libunwindstack/tests/UnwinderTest.cpp diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp index b4481cce9..41153ce19 100644 --- a/libbacktrace/UnwindStack.cpp +++ b/libbacktrace/UnwindStack.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #if !defined(__ANDROID__) @@ -37,6 +38,8 @@ #include #include +#include + #include "BacktraceLog.h" #include "UnwindStack.h" #include "UnwindStackMap.h" @@ -63,135 +66,42 @@ static std::string GetFunctionName(BacktraceMap* back_map, uintptr_t pc, uintptr 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* frames, size_t num_ignore_frames) { + static std::set skip_names{"libunwindstack.so", "libbacktrace.so"}; UnwindStackMap* stack_map = reinterpret_cast(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; diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index ca055161d..97832df70 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -107,6 +107,7 @@ cc_test { "tests/DwarfOpTest.cpp", "tests/DwarfSectionTest.cpp", "tests/DwarfSectionImplTest.cpp", + "tests/ElfFake.cpp", "tests/ElfInterfaceArmTest.cpp", "tests/ElfInterfaceTest.cpp", "tests/ElfTest.cpp", @@ -125,6 +126,7 @@ cc_test { "tests/RegsTest.cpp", "tests/SymbolsTest.cpp", "tests/UnwindTest.cpp", + "tests/UnwinderTest.cpp", ], cflags: [ diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp index 8c6172e77..69e6512dd 100644 --- a/libunwindstack/Regs.cpp +++ b/libunwindstack/Regs.cpp @@ -33,26 +33,6 @@ namespace unwindstack { -template -bool RegsImpl::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) { - switch (return_loc_.type) { - case LOCATION_REGISTER: - CHECK(return_loc_.value < total_regs_); - *value = regs_[return_loc_.value]; - return true; - case LOCATION_SP_OFFSET: - AddressType return_value; - if (!memory->Read(sp_ + return_loc_.value, &return_value, sizeof(return_value))) { - return false; - } - *value = return_value; - return true; - case LOCATION_UNKNOWN: - default: - return false; - } -} - RegsArm::RegsArm() : RegsImpl(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {} diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp index 1a86e16f5..e6489279d 100644 --- a/libunwindstack/Unwinder.cpp +++ b/libunwindstack/Unwinder.cpp @@ -14,9 +14,11 @@ * limitations under the License. */ +#define _GNU_SOURCE 1 #include #include #include +#include #include #include @@ -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::FillInFrame(MapInfo* map_info, uint64_t* rel_pc) { } } -void Unwinder::Unwind() { +void Unwinder::Unwind(std::set* 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 @@ void Unwinder::Unwind() { // 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())) { diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h index 385ee1856..9d3150bf6 100644 --- a/libunwindstack/include/unwindstack/Regs.h +++ b/libunwindstack/include/unwindstack/Regs.h @@ -55,8 +55,6 @@ class Regs { virtual uint64_t pc() = 0; virtual uint64_t sp() = 0; - virtual bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) = 0; - virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0; virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0; @@ -86,8 +84,6 @@ class RegsImpl : public Regs { : Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {} virtual ~RegsImpl() = default; - bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) override; - uint64_t pc() override { return pc_; } uint64_t sp() override { return sp_; } diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h index d66fe5550..71703b495 100644 --- a/libunwindstack/include/unwindstack/Unwinder.h +++ b/libunwindstack/include/unwindstack/Unwinder.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -30,6 +31,9 @@ namespace unwindstack { +// Forward declarations. +class Elf; + struct FrameData { size_t num; @@ -44,6 +48,8 @@ struct FrameData { uint64_t map_offset; uint64_t map_start; uint64_t map_end; + uint64_t map_load_bias; + int map_flags; }; class Unwinder { @@ -54,7 +60,7 @@ class Unwinder { } ~Unwinder() = default; - void Unwind(); + void Unwind(std::set* initial_map_names_to_skip = nullptr); size_t NumFrames() { return frames_.size(); } @@ -64,7 +70,7 @@ class Unwinder { static std::string FormatFrame(const FrameData& frame, bool bits32); private: - void FillInFrame(MapInfo* map_info, uint64_t* rel_pc); + void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc); size_t max_frames_; Maps* maps_; diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp index 69813e5ce..90baabedc 100644 --- a/libunwindstack/tests/DwarfDebugFrameTest.cpp +++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp @@ -25,7 +25,6 @@ #include "LogFake.h" #include "MemoryFake.h" -#include "RegsFake.h" namespace unwindstack { diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp index 07159b0e0..21114da8c 100644 --- a/libunwindstack/tests/DwarfEhFrameTest.cpp +++ b/libunwindstack/tests/DwarfEhFrameTest.cpp @@ -25,7 +25,6 @@ #include "LogFake.h" #include "MemoryFake.h" -#include "RegsFake.h" namespace unwindstack { diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp index 47a40cfde..2d5007b8f 100644 --- a/libunwindstack/tests/DwarfOpTest.cpp +++ b/libunwindstack/tests/DwarfOpTest.cpp @@ -1486,7 +1486,7 @@ TYPED_TEST_P(DwarfOpTest, op_breg) { } this->op_memory_.SetMemory(0, opcode_buffer); - RegsFake regs(32, 10); + RegsImplFake regs(32, 10); for (size_t i = 0; i < 32; i++) { regs[i] = i + 10; } @@ -1518,7 +1518,7 @@ TYPED_TEST_P(DwarfOpTest, op_breg_invalid_register) { }; this->op_memory_.SetMemory(0, opcode_buffer); - RegsFake regs(16, 10); + RegsImplFake regs(16, 10); for (size_t i = 0; i < 16; i++) { regs[i] = i + 10; } @@ -1544,7 +1544,7 @@ TYPED_TEST_P(DwarfOpTest, op_bregx) { 0x92, 0x80, 0x15, 0x80, 0x02}; this->op_memory_.SetMemory(0, opcode_buffer); - RegsFake regs(10, 10); + RegsImplFake regs(10, 10); regs[5] = 0x45; regs[6] = 0x190; this->op_->set_regs(®s); diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp index 2939126dc..c701a295e 100644 --- a/libunwindstack/tests/DwarfSectionImplTest.cpp +++ b/libunwindstack/tests/DwarfSectionImplTest.cpp @@ -90,7 +90,7 @@ TYPED_TEST_CASE_P(DwarfSectionImplTest); TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) { DwarfCie cie{.version = 3, .return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -105,7 +105,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) { TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) { DwarfCie cie{.version = 3, .return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -121,7 +121,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) { TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) { DwarfCie cie{.version = 3, .return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -141,7 +141,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) { TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) { DwarfCie cie{.version = 3, .return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -159,7 +159,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) { TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) { DwarfCie cie{.version = 3, .return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -175,7 +175,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) { TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) { DwarfCie cie{.return_address_register = 60}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; bool finished; @@ -185,7 +185,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) { TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) { DwarfCie cie{.return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; bool finished; @@ -195,7 +195,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) { TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) { DwarfCie cie{.return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}}; @@ -224,7 +224,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) { TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) { DwarfCie cie{.return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -241,7 +241,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) { TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) { DwarfCie cie{.return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -259,7 +259,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) { TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) { DwarfCie cie{.return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -275,7 +275,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) { TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) { DwarfCie cie{.return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -290,7 +290,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) { TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) { DwarfCie cie{.return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; if (sizeof(TypeParam) == sizeof(uint64_t)) { @@ -324,7 +324,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) { TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) { DwarfCie cie{.return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -342,7 +342,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) { TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) { DwarfCie cie{.return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -359,7 +359,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) { TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) { DwarfCie cie{.return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -378,7 +378,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) { TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) { DwarfCie cie{.version = 3, .return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -398,7 +398,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) { TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) { DwarfCie cie{.version = 3, .return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); @@ -416,7 +416,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) { TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) { DwarfCie cie{.version = 3, .return_address_register = 5}; - RegsFake regs(10, 9); + RegsImplFake regs(10, 9); dwarf_loc_regs_t loc_regs; regs.set_pc(0x100); diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp new file mode 100644 index 000000000..71f7f6b23 --- /dev/null +++ b/libunwindstack/tests/ElfFake.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#include "ElfFake.h" +#include "RegsFake.h" + +namespace unwindstack { + +std::deque ElfInterfaceFake::functions_; +std::deque ElfInterfaceFake::steps_; + +bool ElfInterfaceFake::GetFunctionName(uint64_t, std::string* name, uint64_t* offset) { + if (functions_.empty()) { + return false; + } + auto entry = functions_.front(); + functions_.pop_front(); + *name = entry.name; + *offset = entry.offset; + return true; +} + +bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) { + if (steps_.empty()) { + return false; + } + auto entry = steps_.front(); + steps_.pop_front(); + + if (entry.pc == 0 && entry.sp == 0 && !entry.finished) { + // Pretend as though there is no frame. + return false; + } + + RegsFake* fake_regs = reinterpret_cast(regs); + fake_regs->FakeSetPc(entry.pc); + fake_regs->FakeSetSp(entry.sp); + *finished = entry.finished; + return true; +} + +} // namespace unwindstack diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h new file mode 100644 index 000000000..4359bca4e --- /dev/null +++ b/libunwindstack/tests/ElfFake.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_TESTS_ELF_FAKE_H +#define _LIBUNWINDSTACK_TESTS_ELF_FAKE_H + +#include + +#include +#include + +#include +#include +#include +#include + +namespace unwindstack { + +struct StepData { + StepData(uint64_t pc, uint64_t sp, bool finished) : pc(pc), sp(sp), finished(finished) {} + uint64_t pc; + uint64_t sp; + bool finished; +}; + +struct FunctionData { + FunctionData(std::string name, uint64_t offset) : name(name), offset(offset) {} + + std::string name; + uint64_t offset; +}; + +class ElfFake : public Elf { + public: + ElfFake(Memory* memory) : Elf(memory) { valid_ = true; } + virtual ~ElfFake() = default; + + void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); } +}; + +class ElfInterfaceFake : public ElfInterface { + public: + ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {} + virtual ~ElfInterfaceFake() = default; + + bool Init() override { return false; } + void InitHeaders() override {} + bool GetSoname(std::string*) override { return false; } + + bool GetFunctionName(uint64_t, std::string*, uint64_t*) override; + + bool Step(uint64_t, Regs*, Memory*, bool*) override; + + void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; } + + static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); } + static void FakePushStepData(const StepData data) { steps_.push_back(data); } + + static void FakeClear() { + functions_.clear(); + steps_.clear(); + } + + private: + static std::deque functions_; + static std::deque steps_; +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_TESTS_ELF_FAKE_H diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h index b667ec125..efcd029ab 100644 --- a/libunwindstack/tests/RegsFake.h +++ b/libunwindstack/tests/RegsFake.h @@ -24,20 +24,60 @@ namespace unwindstack { -template -class RegsFake : public RegsImpl { +class RegsFake : public Regs { public: RegsFake(uint16_t total_regs, uint16_t sp_reg) - : RegsImpl(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {} + : Regs(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {} virtual ~RegsFake() = default; + uint32_t MachineType() override { return fake_type_; } + void* RawData() override { return nullptr; } + uint64_t pc() override { return fake_pc_; } + uint64_t sp() override { return fake_sp_; } + bool SetPcFromReturnAddress(Memory*) override { + if (!fake_return_address_valid_) { + return false; + } + fake_pc_ = fake_return_address_; + return true; + } + + uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; } + + bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; } + + void SetFromRaw() override {} + + void FakeSetMachineType(uint32_t type) { fake_type_ = type; } + void FakeSetPc(uint64_t pc) { fake_pc_ = pc; } + void FakeSetSp(uint64_t sp) { fake_sp_ = sp; } + void FakeSetReturnAddress(uint64_t return_address) { fake_return_address_ = return_address; } + void FakeSetReturnAddressValid(bool valid) { fake_return_address_valid_ = valid; } + + private: + uint32_t fake_type_ = 0; + uint64_t fake_pc_ = 0; + uint64_t fake_sp_ = 0; + bool fake_return_address_valid_ = false; + uint64_t fake_return_address_ = 0; +}; + +template +class RegsImplFake : public RegsImpl { + public: + RegsImplFake(uint16_t total_regs, uint16_t sp_reg) + : RegsImpl(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {} + virtual ~RegsImplFake() = default; + uint32_t MachineType() override { return 0; } uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; } void SetFromRaw() override {} bool SetPcFromReturnAddress(Memory*) override { return false; } bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; } - bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; } + + void FakeSetPc(uint64_t pc) { this->pc_ = pc; } + void FakeSetSp(uint64_t sp) { this->sp_ = sp; } }; } // namespace unwindstack diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp index 3912e176d..3b9f92b01 100644 --- a/libunwindstack/tests/RegsTest.cpp +++ b/libunwindstack/tests/RegsTest.cpp @@ -23,68 +23,28 @@ #include #include +#include "ElfFake.h" #include "MemoryFake.h" +#include "RegsFake.h" namespace unwindstack { -class ElfFake : public Elf { - public: - ElfFake(Memory* memory) : Elf(memory) { valid_ = true; } - virtual ~ElfFake() = default; - - void set_elf_interface(ElfInterface* interface) { interface_.reset(interface); } -}; - -class ElfInterfaceFake : public ElfInterface { - public: - ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {} - virtual ~ElfInterfaceFake() = default; - - void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; } - - bool Init() override { return false; } - void InitHeaders() override {} - bool GetSoname(std::string*) override { return false; } - bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; } - bool Step(uint64_t, Regs*, Memory*, bool*) override { return false; } -}; - -template -class RegsTestImpl : public RegsImpl { - public: - RegsTestImpl(uint16_t total_regs, uint16_t regs_sp) - : RegsImpl(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {} - RegsTestImpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc) - : RegsImpl(total_regs, regs_sp, return_loc) {} - virtual ~RegsTestImpl() = default; - - uint32_t MachineType() override { return 0; } - - uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; } - void SetFromRaw() override {} - bool SetPcFromReturnAddress(Memory*) override { return false; } - bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; } -}; - class RegsTest : public ::testing::Test { protected: void SetUp() override { memory_ = new MemoryFake; elf_.reset(new ElfFake(memory_)); elf_interface_ = new ElfInterfaceFake(elf_->memory()); - elf_->set_elf_interface(elf_interface_); + elf_->FakeSetInterface(elf_interface_); } - template - void RegsReturnAddressRegister(); - ElfInterfaceFake* elf_interface_; MemoryFake* memory_; std::unique_ptr elf_; }; TEST_F(RegsTest, regs32) { - RegsTestImpl regs32(50, 10); + RegsImplFake regs32(50, 10); ASSERT_EQ(50U, regs32.total_regs()); ASSERT_EQ(10U, regs32.sp_reg()); @@ -107,7 +67,7 @@ TEST_F(RegsTest, regs32) { } TEST_F(RegsTest, regs64) { - RegsTestImpl regs64(30, 12); + RegsImplFake regs64(30, 12); ASSERT_EQ(30U, regs64.total_regs()); ASSERT_EQ(12U, regs64.sp_reg()); @@ -129,44 +89,6 @@ TEST_F(RegsTest, regs64) { ASSERT_EQ(10U, regs64[8]); } -template -void RegsTest::RegsReturnAddressRegister() { - RegsTestImpl regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5)); - - regs[5] = 0x12345; - uint64_t value; - ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value)); - ASSERT_EQ(0x12345U, value); -} - -TEST_F(RegsTest, regs32_return_address_register) { - RegsReturnAddressRegister(); -} - -TEST_F(RegsTest, regs64_return_address_register) { - RegsReturnAddressRegister(); -} - -TEST_F(RegsTest, regs32_return_address_sp_offset) { - RegsTestImpl regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2)); - - regs.set_sp(0x2002); - memory_->SetData32(0x2000, 0x12345678); - uint64_t value; - ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value)); - ASSERT_EQ(0x12345678U, value); -} - -TEST_F(RegsTest, regs64_return_address_sp_offset) { - RegsTestImpl regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8)); - - regs.set_sp(0x2008); - memory_->SetData64(0x2000, 0x12345678aabbccddULL); - uint64_t value; - ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value)); - ASSERT_EQ(0x12345678aabbccddULL, value); -} - TEST_F(RegsTest, rel_pc) { RegsArm64 arm64; ASSERT_EQ(0xcU, arm64.GetAdjustedPc(0x10, elf_.get())); @@ -193,7 +115,7 @@ TEST_F(RegsTest, rel_pc_arm) { RegsArm arm; // Check fence posts. - elf_interface_->set_load_bias(0); + elf_interface_->FakeSetLoadBias(0); ASSERT_EQ(3U, arm.GetAdjustedPc(0x5, elf_.get())); ASSERT_EQ(4U, arm.GetAdjustedPc(0x4, elf_.get())); ASSERT_EQ(3U, arm.GetAdjustedPc(0x3, elf_.get())); @@ -201,7 +123,7 @@ TEST_F(RegsTest, rel_pc_arm) { ASSERT_EQ(1U, arm.GetAdjustedPc(0x1, elf_.get())); ASSERT_EQ(0U, arm.GetAdjustedPc(0x0, elf_.get())); - elf_interface_->set_load_bias(0x100); + elf_interface_->FakeSetLoadBias(0x100); ASSERT_EQ(0xffU, arm.GetAdjustedPc(0xff, elf_.get())); ASSERT_EQ(0x103U, arm.GetAdjustedPc(0x105, elf_.get())); ASSERT_EQ(0x104U, arm.GetAdjustedPc(0x104, elf_.get())); @@ -211,13 +133,13 @@ TEST_F(RegsTest, rel_pc_arm) { ASSERT_EQ(0x100U, arm.GetAdjustedPc(0x100, elf_.get())); // Check thumb instructions handling. - elf_interface_->set_load_bias(0); + elf_interface_->FakeSetLoadBias(0); memory_->SetData32(0x2000, 0); ASSERT_EQ(0x2003U, arm.GetAdjustedPc(0x2005, elf_.get())); memory_->SetData32(0x2000, 0xe000f000); ASSERT_EQ(0x2001U, arm.GetAdjustedPc(0x2005, elf_.get())); - elf_interface_->set_load_bias(0x400); + elf_interface_->FakeSetLoadBias(0x400); memory_->SetData32(0x2100, 0); ASSERT_EQ(0x2503U, arm.GetAdjustedPc(0x2505, elf_.get())); memory_->SetData32(0x2100, 0xf111f111); diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp new file mode 100644 index 000000000..4d0366ca5 --- /dev/null +++ b/libunwindstack/tests/UnwinderTest.cpp @@ -0,0 +1,576 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "ElfFake.h" +#include "MemoryFake.h" +#include "RegsFake.h" + +namespace unwindstack { + +class MapsFake : public Maps { + public: + MapsFake() = default; + virtual ~MapsFake() = default; + + bool Parse() { return true; } + + void FakeClear() { maps_.clear(); } + + void FakeAddMapInfo(const MapInfo& map_info) { maps_.push_back(map_info); } +}; + +class UnwinderTest : public ::testing::Test { + protected: + static void SetUpTestCase() { + maps_.FakeClear(); + MapInfo info; + info.name = "/system/fake/libc.so"; + info.start = 0x1000; + info.end = 0x8000; + info.offset = 0; + info.flags = PROT_READ | PROT_WRITE; + ElfFake* elf = new ElfFake(nullptr); + info.elf = elf; + elf->FakeSetInterface(new ElfInterfaceFake(nullptr)); + info.elf_offset = 0; + maps_.FakeAddMapInfo(info); + + info.name = "[stack]"; + info.start = 0x10000; + info.end = 0x12000; + info.flags = PROT_READ | PROT_WRITE; + info.elf = nullptr; + maps_.FakeAddMapInfo(info); + + info.name = "/dev/fake_device"; + info.start = 0x13000; + info.end = 0x15000; + info.flags = PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP; + info.elf = nullptr; + maps_.FakeAddMapInfo(info); + + info.name = "/system/fake/libunwind.so"; + info.start = 0x20000; + info.end = 0x22000; + info.flags = PROT_READ | PROT_WRITE; + elf = new ElfFake(nullptr); + info.elf = elf; + elf->FakeSetInterface(new ElfInterfaceFake(nullptr)); + maps_.FakeAddMapInfo(info); + + info.name = "/fake/libanother.so"; + info.start = 0x23000; + info.end = 0x24000; + info.flags = PROT_READ | PROT_WRITE; + elf = new ElfFake(nullptr); + info.elf = elf; + elf->FakeSetInterface(new ElfInterfaceFake(nullptr)); + maps_.FakeAddMapInfo(info); + } + + void SetUp() override { + ElfInterfaceFake::FakeClear(); + regs_.FakeSetMachineType(EM_ARM); + } + + static MapsFake maps_; + static RegsFake regs_; + static std::shared_ptr process_memory_; +}; + +MapsFake UnwinderTest::maps_; +RegsFake UnwinderTest::regs_(5, 0); +std::shared_ptr UnwinderTest::process_memory_(nullptr); + +TEST_F(UnwinderTest, multiple_frames) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2)); + + regs_.FakeSetPc(0x1000); + regs_.FakeSetSp(0x10000); + ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false)); + ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false)); + ElfInterfaceFake::FakePushStepData(StepData(0, 0, true)); + + Unwinder unwinder(64, &maps_, ®s_, process_memory_); + unwinder.Unwind(); + + ASSERT_EQ(3U, unwinder.NumFrames()); + + auto* frame = &unwinder.frames()[0]; + EXPECT_EQ(0U, frame->num); + EXPECT_EQ(0U, frame->rel_pc); + EXPECT_EQ(0x1000U, frame->pc); + EXPECT_EQ(0x10000U, frame->sp); + EXPECT_EQ("Frame0", frame->function_name); + EXPECT_EQ(0U, frame->function_offset); + EXPECT_EQ("/system/fake/libc.so", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0x1000U, frame->map_start); + EXPECT_EQ(0x8000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags); + + frame = &unwinder.frames()[1]; + EXPECT_EQ(1U, frame->num); + EXPECT_EQ(0x100U, frame->rel_pc); + EXPECT_EQ(0x1100U, frame->pc); + EXPECT_EQ(0x10010U, frame->sp); + EXPECT_EQ("Frame1", frame->function_name); + EXPECT_EQ(1U, frame->function_offset); + EXPECT_EQ("/system/fake/libc.so", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0x1000U, frame->map_start); + EXPECT_EQ(0x8000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags); + + frame = &unwinder.frames()[2]; + EXPECT_EQ(2U, frame->num); + EXPECT_EQ(0x200U, frame->rel_pc); + EXPECT_EQ(0x1200U, frame->pc); + EXPECT_EQ(0x10020U, frame->sp); + EXPECT_EQ("Frame2", frame->function_name); + EXPECT_EQ(2U, frame->function_offset); + EXPECT_EQ("/system/fake/libc.so", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0x1000U, frame->map_start); + EXPECT_EQ(0x8000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags); +} + +// Verify that no attempt to continue after the step indicates it is done. +TEST_F(UnwinderTest, no_frames_after_finished) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4)); + + regs_.FakeSetPc(0x1000); + regs_.FakeSetSp(0x10000); + ElfInterfaceFake::FakePushStepData(StepData(0x1000, 0x10000, true)); + ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false)); + ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false)); + + Unwinder unwinder(64, &maps_, ®s_, process_memory_); + unwinder.Unwind(); + + ASSERT_EQ(1U, unwinder.NumFrames()); + + auto* frame = &unwinder.frames()[0]; + EXPECT_EQ(0U, frame->num); + EXPECT_EQ(0U, frame->rel_pc); + EXPECT_EQ(0x1000U, frame->pc); + EXPECT_EQ(0x10000U, frame->sp); + EXPECT_EQ("Frame0", frame->function_name); + EXPECT_EQ(0U, frame->function_offset); + EXPECT_EQ("/system/fake/libc.so", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0x1000U, frame->map_start); + EXPECT_EQ(0x8000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags); +} + +// Verify the maximum frames to save. +TEST_F(UnwinderTest, max_frames) { + for (size_t i = 0; i < 30; i++) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame" + std::to_string(i), i)); + ElfInterfaceFake::FakePushStepData(StepData(0x1102 + i * 0x100, 0x10010 + i * 0x10, false)); + } + + regs_.FakeSetPc(0x1000); + regs_.FakeSetSp(0x10000); + + Unwinder unwinder(20, &maps_, ®s_, process_memory_); + unwinder.Unwind(); + + ASSERT_EQ(20U, unwinder.NumFrames()); + + for (size_t i = 0; i < 20; i++) { + auto* frame = &unwinder.frames()[i]; + EXPECT_EQ(i, frame->num); + EXPECT_EQ(i * 0x100, frame->rel_pc) << "Failed at frame " << i; + EXPECT_EQ(0x1000 + i * 0x100, frame->pc) << "Failed at frame " << i; + EXPECT_EQ(0x10000 + 0x10 * i, frame->sp) << "Failed at frame " << i; + EXPECT_EQ("Frame" + std::to_string(i), frame->function_name) << "Failed at frame " << i; + EXPECT_EQ(i, frame->function_offset) << "Failed at frame " << i; + EXPECT_EQ("/system/fake/libc.so", frame->map_name) << "Failed at frame " << i; + EXPECT_EQ(0U, frame->map_offset) << "Failed at frame " << i; + EXPECT_EQ(0x1000U, frame->map_start) << "Failed at frame " << i; + EXPECT_EQ(0x8000U, frame->map_end) << "Failed at frame " << i; + EXPECT_EQ(0U, frame->map_load_bias) << "Failed at frame " << i; + EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags) << "Failed at frame " << i; + } +} + +// Verify that initial map names frames are removed. +TEST_F(UnwinderTest, verify_frames_skipped) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2)); + + regs_.FakeSetPc(0x20000); + regs_.FakeSetSp(0x10000); + ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false)); + ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false)); + ElfInterfaceFake::FakePushStepData(StepData(0x20002, 0x10030, false)); + ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10040, false)); + ElfInterfaceFake::FakePushStepData(StepData(0x1002, 0x10050, false)); + ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10060, false)); + ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10070, false)); + ElfInterfaceFake::FakePushStepData(StepData(0, 0, true)); + + Unwinder unwinder(64, &maps_, ®s_, process_memory_); + std::set skip_set{"libunwind.so", "libanother.so"}; + unwinder.Unwind(&skip_set); + + ASSERT_EQ(3U, unwinder.NumFrames()); + + auto* frame = &unwinder.frames()[0]; + EXPECT_EQ(0U, frame->num); + EXPECT_EQ(0U, frame->rel_pc); + EXPECT_EQ(0x1000U, frame->pc); + EXPECT_EQ(0x10050U, frame->sp); + EXPECT_EQ("Frame0", frame->function_name); + EXPECT_EQ(0U, frame->function_offset); + EXPECT_EQ("/system/fake/libc.so", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0x1000U, frame->map_start); + EXPECT_EQ(0x8000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags); + + frame = &unwinder.frames()[1]; + EXPECT_EQ(1U, frame->num); + EXPECT_EQ(0x1000U, frame->rel_pc); + EXPECT_EQ(0x21000U, frame->pc); + EXPECT_EQ(0x10060U, frame->sp); + EXPECT_EQ("Frame1", frame->function_name); + EXPECT_EQ(1U, frame->function_offset); + EXPECT_EQ("/system/fake/libunwind.so", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0x20000U, frame->map_start); + EXPECT_EQ(0x22000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags); + + frame = &unwinder.frames()[2]; + EXPECT_EQ(2U, frame->num); + EXPECT_EQ(0U, frame->rel_pc); + EXPECT_EQ(0x23000U, frame->pc); + EXPECT_EQ(0x10070U, frame->sp); + EXPECT_EQ("Frame2", frame->function_name); + EXPECT_EQ(2U, frame->function_offset); + EXPECT_EQ("/fake/libanother.so", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0x23000U, frame->map_start); + EXPECT_EQ(0x24000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags); +} + +// Verify SP in a non-existant map is okay. +TEST_F(UnwinderTest, sp_not_in_map) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1)); + + regs_.FakeSetPc(0x1000); + regs_.FakeSetSp(0x53000); + ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false)); + ElfInterfaceFake::FakePushStepData(StepData(0, 0, true)); + + Unwinder unwinder(64, &maps_, ®s_, process_memory_); + unwinder.Unwind(); + + ASSERT_EQ(2U, unwinder.NumFrames()); + + auto* frame = &unwinder.frames()[0]; + EXPECT_EQ(0U, frame->num); + EXPECT_EQ(0U, frame->rel_pc); + EXPECT_EQ(0x1000U, frame->pc); + EXPECT_EQ(0x53000U, frame->sp); + EXPECT_EQ("Frame0", frame->function_name); + EXPECT_EQ(0U, frame->function_offset); + EXPECT_EQ("/system/fake/libc.so", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0x1000U, frame->map_start); + EXPECT_EQ(0x8000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags); + + frame = &unwinder.frames()[1]; + EXPECT_EQ(1U, frame->num); + EXPECT_EQ(0x1000U, frame->rel_pc); + EXPECT_EQ(0x21000U, frame->pc); + EXPECT_EQ(0x50020U, frame->sp); + EXPECT_EQ("Frame1", frame->function_name); + EXPECT_EQ(1U, frame->function_offset); + EXPECT_EQ("/system/fake/libunwind.so", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0x20000U, frame->map_start); + EXPECT_EQ(0x22000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags); +} + +// Verify PC in a device stops the unwind. +TEST_F(UnwinderTest, pc_in_device_stops_unwind) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2)); + + regs_.FakeSetPc(0x13000); + regs_.FakeSetSp(0x10000); + ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false)); + ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false)); + ElfInterfaceFake::FakePushStepData(StepData(0, 0, true)); + + Unwinder unwinder(64, &maps_, ®s_, process_memory_); + unwinder.Unwind(); + + ASSERT_EQ(1U, unwinder.NumFrames()); +} + +// Verify SP in a device stops the unwind. +TEST_F(UnwinderTest, sp_in_device_stops_unwind) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2)); + + regs_.FakeSetPc(0x1000); + regs_.FakeSetSp(0x13000); + ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false)); + ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false)); + ElfInterfaceFake::FakePushStepData(StepData(0, 0, true)); + + Unwinder unwinder(64, &maps_, ®s_, process_memory_); + unwinder.Unwind(); + + ASSERT_EQ(1U, unwinder.NumFrames()); +} + +// Verify a no map info frame gets a frame. +TEST_F(UnwinderTest, pc_without_map) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0)); + + regs_.FakeSetPc(0x41000); + regs_.FakeSetSp(0x13000); + + Unwinder unwinder(64, &maps_, ®s_, process_memory_); + unwinder.Unwind(); + + ASSERT_EQ(1U, unwinder.NumFrames()); + + auto* frame = &unwinder.frames()[0]; + EXPECT_EQ(0U, frame->num); + EXPECT_EQ(0x41000U, frame->rel_pc); + EXPECT_EQ(0x41000U, frame->pc); + EXPECT_EQ(0x13000U, frame->sp); + EXPECT_EQ("", frame->function_name); + EXPECT_EQ(0U, frame->function_offset); + EXPECT_EQ("", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0U, frame->map_start); + EXPECT_EQ(0U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(0, frame->map_flags); +} + +// Verify that a speculative frame is added. +TEST_F(UnwinderTest, speculative_frame) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1)); + + // Fake as if code called a nullptr function. + regs_.FakeSetPc(0); + regs_.FakeSetSp(0x10000); + regs_.FakeSetReturnAddress(0x1202); + regs_.FakeSetReturnAddressValid(true); + + ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false)); + ElfInterfaceFake::FakePushStepData(StepData(0, 0, true)); + + Unwinder unwinder(64, &maps_, ®s_, process_memory_); + unwinder.Unwind(); + + ASSERT_EQ(3U, unwinder.NumFrames()); + + auto* frame = &unwinder.frames()[0]; + EXPECT_EQ(0U, frame->num); + EXPECT_EQ(0U, frame->rel_pc); + EXPECT_EQ(0U, frame->pc); + EXPECT_EQ(0x10000U, frame->sp); + EXPECT_EQ("", frame->function_name); + EXPECT_EQ(0U, frame->function_offset); + EXPECT_EQ("", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0U, frame->map_start); + EXPECT_EQ(0U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(0, frame->map_flags); + + frame = &unwinder.frames()[1]; + EXPECT_EQ(1U, frame->num); + EXPECT_EQ(0x200U, frame->rel_pc); + EXPECT_EQ(0x1200U, frame->pc); + EXPECT_EQ(0x10000U, frame->sp); + EXPECT_EQ("Frame0", frame->function_name); + EXPECT_EQ(0U, frame->function_offset); + EXPECT_EQ("/system/fake/libc.so", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0x1000U, frame->map_start); + EXPECT_EQ(0x8000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags); + + frame = &unwinder.frames()[2]; + EXPECT_EQ(2U, frame->num); + EXPECT_EQ(0x100U, frame->rel_pc); + EXPECT_EQ(0x23100U, frame->pc); + EXPECT_EQ(0x10020U, frame->sp); + EXPECT_EQ("Frame1", frame->function_name); + EXPECT_EQ(1U, frame->function_offset); + EXPECT_EQ("/fake/libanother.so", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0x23000U, frame->map_start); + EXPECT_EQ(0x24000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags); +} + +// Verify that a speculative frame is added then removed because no other +// frames are added. +TEST_F(UnwinderTest, speculative_frame_removed) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0)); + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1)); + + // Fake as if code called a nullptr function. + regs_.FakeSetPc(0); + regs_.FakeSetSp(0x10000); + regs_.FakeSetReturnAddress(0x1202); + regs_.FakeSetReturnAddressValid(true); + + Unwinder unwinder(64, &maps_, ®s_, process_memory_); + unwinder.Unwind(); + + ASSERT_EQ(1U, unwinder.NumFrames()); + + auto* frame = &unwinder.frames()[0]; + EXPECT_EQ(0U, frame->num); + EXPECT_EQ(0U, frame->rel_pc); + EXPECT_EQ(0U, frame->pc); + EXPECT_EQ(0x10000U, frame->sp); + EXPECT_EQ("", frame->function_name); + EXPECT_EQ(0U, frame->function_offset); + EXPECT_EQ("", frame->map_name); + EXPECT_EQ(0U, frame->map_offset); + EXPECT_EQ(0U, frame->map_start); + EXPECT_EQ(0U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(0, frame->map_flags); +} + +// Verify format frame code. +TEST_F(UnwinderTest, format_frame_static) { + FrameData frame; + frame.num = 1; + frame.rel_pc = 0x1000; + frame.pc = 0x4000; + frame.sp = 0x1000; + frame.function_name = "function"; + frame.function_offset = 100; + frame.map_name = "/fake/libfake.so"; + frame.map_offset = 0x2000; + frame.map_start = 0x3000; + frame.map_end = 0x6000; + frame.map_flags = PROT_READ; + + EXPECT_EQ(" #01 pc 0000000000001000 (offset 0x2000) /fake/libfake.so (function+100)", + Unwinder::FormatFrame(frame, false)); + EXPECT_EQ(" #01 pc 00001000 (offset 0x2000) /fake/libfake.so (function+100)", + Unwinder::FormatFrame(frame, true)); + + frame.map_offset = 0; + EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function+100)", + Unwinder::FormatFrame(frame, false)); + EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)", + Unwinder::FormatFrame(frame, true)); + + frame.function_offset = 0; + EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function)", + Unwinder::FormatFrame(frame, false)); + EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function)", Unwinder::FormatFrame(frame, true)); + + frame.function_name = ""; + EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so", Unwinder::FormatFrame(frame, false)); + EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so", Unwinder::FormatFrame(frame, true)); + + frame.map_name = ""; + EXPECT_EQ(" #01 pc 0000000000001000 ", Unwinder::FormatFrame(frame, false)); + EXPECT_EQ(" #01 pc 00001000 ", Unwinder::FormatFrame(frame, true)); + + frame.map_start = 0; + frame.map_end = 0; + EXPECT_EQ(" #01 pc 0000000000001000 ", Unwinder::FormatFrame(frame, false)); + EXPECT_EQ(" #01 pc 00001000 ", Unwinder::FormatFrame(frame, true)); +} + +// Verify format frame code. +TEST_F(UnwinderTest, format_frame) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10)); + + regs_.FakeSetPc(0x2300); + regs_.FakeSetSp(0x10000); + + Unwinder unwinder(64, &maps_, ®s_, process_memory_); + unwinder.Unwind(); + + ASSERT_EQ(1U, unwinder.NumFrames()); + + regs_.FakeSetMachineType(EM_ARM); + EXPECT_EQ(" #00 pc 00001300 /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0)); + regs_.FakeSetMachineType(EM_386); + EXPECT_EQ(" #00 pc 00001300 /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0)); + + regs_.FakeSetMachineType(EM_AARCH64); + EXPECT_EQ(" #00 pc 0000000000001300 /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0)); + regs_.FakeSetMachineType(EM_X86_64); + EXPECT_EQ(" #00 pc 0000000000001300 /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0)); + + EXPECT_EQ("", unwinder.FormatFrame(1)); +} + +} // namespace unwindstack