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
This commit is contained in:
parent
dea5e081ac
commit
f6f691b63c
15 changed files with 894 additions and 277 deletions
|
@ -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 @@ 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<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;
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -33,26 +33,6 @@
|
|||
|
||||
namespace unwindstack {
|
||||
|
||||
template <typename AddressType>
|
||||
bool RegsImpl<AddressType>::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<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
|
||||
|
||||
|
|
|
@ -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::FillInFrame(MapInfo* map_info, uint64_t* rel_pc) {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ 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())) {
|
||||
|
|
|
@ -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_; }
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -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<std::string>* 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_;
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
#include "LogFake.h"
|
||||
#include "MemoryFake.h"
|
||||
#include "RegsFake.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
#include "LogFake.h"
|
||||
#include "MemoryFake.h"
|
||||
#include "RegsFake.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
|
|
@ -1486,7 +1486,7 @@ TYPED_TEST_P(DwarfOpTest, op_breg) {
|
|||
}
|
||||
this->op_memory_.SetMemory(0, opcode_buffer);
|
||||
|
||||
RegsFake<TypeParam> regs(32, 10);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(16, 10);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 10);
|
||||
RegsImplFake<TypeParam> regs(10, 10);
|
||||
regs[5] = 0x45;
|
||||
regs[6] = 0x190;
|
||||
this->op_->set_regs(®s);
|
||||
|
|
|
@ -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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> 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<TypeParam> regs(10, 9);
|
||||
RegsImplFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
|
|
65
libunwindstack/tests/ElfFake.cpp
Normal file
65
libunwindstack/tests/ElfFake.cpp
Normal file
|
@ -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 <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/ElfInterface.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
|
||||
#include "ElfFake.h"
|
||||
#include "RegsFake.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
std::deque<FunctionData> ElfInterfaceFake::functions_;
|
||||
std::deque<StepData> 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<RegsFake*>(regs);
|
||||
fake_regs->FakeSetPc(entry.pc);
|
||||
fake_regs->FakeSetSp(entry.sp);
|
||||
*finished = entry.finished;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
84
libunwindstack/tests/ElfFake.h
Normal file
84
libunwindstack/tests/ElfFake.h
Normal file
|
@ -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 <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/ElfInterface.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
|
||||
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<FunctionData> functions_;
|
||||
static std::deque<StepData> steps_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
|
|
@ -24,20 +24,60 @@
|
|||
|
||||
namespace unwindstack {
|
||||
|
||||
template <typename TypeParam>
|
||||
class RegsFake : public RegsImpl<TypeParam> {
|
||||
class RegsFake : public Regs {
|
||||
public:
|
||||
RegsFake(uint16_t total_regs, uint16_t sp_reg)
|
||||
: RegsImpl<TypeParam>(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 <typename TypeParam>
|
||||
class RegsImplFake : public RegsImpl<TypeParam> {
|
||||
public:
|
||||
RegsImplFake(uint16_t total_regs, uint16_t sp_reg)
|
||||
: RegsImpl<TypeParam>(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
|
||||
|
|
|
@ -23,68 +23,28 @@
|
|||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
|
||||
#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 <typename TypeParam>
|
||||
class RegsTestImpl : public RegsImpl<TypeParam> {
|
||||
public:
|
||||
RegsTestImpl(uint16_t total_regs, uint16_t regs_sp)
|
||||
: RegsImpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
|
||||
RegsTestImpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
|
||||
: RegsImpl<TypeParam>(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 <typename AddressType>
|
||||
void RegsReturnAddressRegister();
|
||||
|
||||
ElfInterfaceFake* elf_interface_;
|
||||
MemoryFake* memory_;
|
||||
std::unique_ptr<ElfFake> elf_;
|
||||
};
|
||||
|
||||
TEST_F(RegsTest, regs32) {
|
||||
RegsTestImpl<uint32_t> regs32(50, 10);
|
||||
RegsImplFake<uint32_t> 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<uint64_t> regs64(30, 12);
|
||||
RegsImplFake<uint64_t> 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 <typename AddressType>
|
||||
void RegsTest::RegsReturnAddressRegister() {
|
||||
RegsTestImpl<AddressType> 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<uint32_t>();
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, regs64_return_address_register) {
|
||||
RegsReturnAddressRegister<uint64_t>();
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, regs32_return_address_sp_offset) {
|
||||
RegsTestImpl<uint32_t> 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<uint64_t> 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);
|
||||
|
|
576
libunwindstack/tests/UnwinderTest.cpp
Normal file
576
libunwindstack/tests/UnwinderTest.cpp
Normal file
|
@ -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 <elf.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
#include <unwindstack/Unwinder.h>
|
||||
|
||||
#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<Memory> process_memory_;
|
||||
};
|
||||
|
||||
MapsFake UnwinderTest::maps_;
|
||||
RegsFake UnwinderTest::regs_(5, 0);
|
||||
std::shared_ptr<Memory> 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<std::string> 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 <anonymous:3000>", Unwinder::FormatFrame(frame, false));
|
||||
EXPECT_EQ(" #01 pc 00001000 <anonymous:3000>", Unwinder::FormatFrame(frame, true));
|
||||
|
||||
frame.map_start = 0;
|
||||
frame.map_end = 0;
|
||||
EXPECT_EQ(" #01 pc 0000000000001000 <unknown>", Unwinder::FormatFrame(frame, false));
|
||||
EXPECT_EQ(" #01 pc 00001000 <unknown>", 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
|
Loading…
Reference in a new issue