Fix incorrect usage of relative pcs.

When stepping, it's necessary to use both the unaltered relative pc
and the adjusted relative pc. If the adjusted pc is not used, the
wrong unwind information can be used.

Added new offline unit tests that take real data and verifies that it
unwinds properly.

Fix a bug in the map code that would not properly parse map data for
a 64 bit process when done in a 32 bit process.

Fix bug in eh_frame processing that didn't adjust the pc correctly.
Fix unit tests related to the pc adjustment.

Bug: 69475565

Test: Passes libbacktrace/libunwindstack unit tests.
Test: Run debuggerd -b on processes on a hikey.
Change-Id: Ic501a1c4549c5f61d2742a7105c42a960f2c892b
This commit is contained in:
Christopher Ferris 2017-11-28 19:14:54 -08:00
parent f819c1d94b
commit c3d79f7c07
22 changed files with 238 additions and 64 deletions

View file

@ -130,6 +130,7 @@ cc_test {
"tests/RegsStepIfSignalHandlerTest.cpp",
"tests/RegsTest.cpp",
"tests/SymbolsTest.cpp",
"tests/UnwindOfflineTest.cpp",
"tests/UnwindTest.cpp",
"tests/UnwinderTest.cpp",
],
@ -153,6 +154,8 @@ cc_test {
data: [
"tests/files/elf32.xz",
"tests/files/elf64.xz",
"tests/files/offline/straddle_arm32/*",
"tests/files/offline/straddle_arm64/*",
],
}

View file

@ -40,7 +40,7 @@ class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
uint64_t AdjustPcFromFde(uint64_t pc) override {
// The eh_frame uses relative pcs.
return pc + this->memory_.cur_offset();
return pc + this->memory_.cur_offset() - 4;
}
};

View file

@ -104,8 +104,8 @@ bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offse
}
// The relative pc is always relative to the start of the map from which it comes.
bool Elf::Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory,
bool* finished) {
bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
Memory* process_memory, bool* finished) {
if (!valid_) {
return false;
}
@ -117,16 +117,16 @@ bool Elf::Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process
}
// Adjust the load bias to get the real relative pc.
if (rel_pc < load_bias_) {
if (adjusted_rel_pc < load_bias_) {
return false;
}
rel_pc -= load_bias_;
adjusted_rel_pc -= load_bias_;
// Lock during the step which can update information in the object.
std::lock_guard<std::mutex> guard(lock_);
return interface_->Step(rel_pc, regs, process_memory, finished) ||
return interface_->Step(adjusted_rel_pc, regs, process_memory, finished) ||
(gnu_debugdata_interface_ &&
gnu_debugdata_interface_->Step(rel_pc, regs, process_memory, finished));
gnu_debugdata_interface_->Step(adjusted_rel_pc, regs, process_memory, finished));
}
bool Elf::IsValidElf(Memory* memory) {

View file

@ -64,13 +64,13 @@ static MapInfo* InternalParseLine(const char* line) {
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
char* str;
const char* old_str = line;
uint64_t start = strtoul(old_str, &str, 16);
uint64_t start = strtoull(old_str, &str, 16);
if (old_str == str || *str++ != '-') {
return nullptr;
}
old_str = str;
uint64_t end = strtoul(old_str, &str, 16);
uint64_t end = strtoull(old_str, &str, 16);
if (old_str == str || !std::isspace(*str++)) {
return nullptr;
}
@ -112,14 +112,14 @@ static MapInfo* InternalParseLine(const char* line) {
}
old_str = str;
uint64_t offset = strtoul(old_str, &str, 16);
uint64_t offset = strtoull(old_str, &str, 16);
if (old_str == str || !std::isspace(*str)) {
return nullptr;
}
// Ignore the 00:00 values.
old_str = str;
(void)strtoul(old_str, &str, 16);
(void)strtoull(old_str, &str, 16);
if (old_str == str || *str++ != ':') {
return nullptr;
}
@ -129,14 +129,14 @@ static MapInfo* InternalParseLine(const char* line) {
// Skip the inode.
old_str = str;
(void)strtoul(str, &str, 16);
(void)strtoull(str, &str, 16);
if (old_str == str || !std::isspace(*str++)) {
return nullptr;
}
// Skip decimal digit.
old_str = str;
(void)strtoul(old_str, &str, 10);
(void)strtoull(old_str, &str, 10);
if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
return nullptr;
}

View file

@ -32,27 +32,20 @@
namespace unwindstack {
void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc) {
void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_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 = rel_pc;
frame->rel_pc = adjusted_rel_pc;
if (map_info == nullptr) {
frame->pc = regs_->pc();
return;
}
if (adjust_pc) {
// Don't adjust the first frame pc.
frame->rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
// Adjust the original pc.
frame->pc -= rel_pc - frame->rel_pc;
}
frame->pc = map_info->start + adjusted_rel_pc;
frame->map_name = map_info->name;
frame->map_offset = map_info->offset;
frame->map_start = map_info->start;
@ -92,21 +85,29 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
MapInfo* map_info = maps_->Find(regs_->pc());
uint64_t rel_pc;
uint64_t adjusted_rel_pc;
Elf* elf;
if (map_info == nullptr) {
rel_pc = regs_->pc();
adjusted_rel_pc = rel_pc;
} else {
if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
break;
}
elf = map_info->GetElf(process_memory_, true);
rel_pc = elf->GetRelPc(regs_->pc(), map_info);
if (adjust_pc) {
adjusted_rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
} else {
adjusted_rel_pc = rel_pc;
}
}
if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
FillInFrame(map_info, elf, rel_pc, adjust_pc);
FillInFrame(map_info, elf, adjusted_rel_pc);
// Once a frame is added, stop skipping frames.
initial_map_names_to_skip = nullptr;
}
@ -133,7 +134,8 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
in_device_map = true;
} else {
bool finished;
stepped = elf->Step(rel_pc, map_info->elf_offset, regs_, process_memory_.get(), &finished);
stepped = elf->Step(rel_pc, adjusted_rel_pc, map_info->elf_offset, regs_,
process_memory_.get(), &finished);
if (stepped && finished) {
break;
}

View file

@ -51,8 +51,8 @@ class Elf {
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
bool Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory,
bool* finished);
bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
Memory* process_memory, bool* finished);
ElfInterface* CreateInterfaceFromMemory(Memory* memory);

View file

@ -70,7 +70,7 @@ class Unwinder {
static std::string FormatFrame(const FrameData& frame, bool bits32);
private:
void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc);
void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc);
size_t max_frames_;
Maps* maps_;

View file

@ -109,23 +109,23 @@ TYPED_TEST_P(DwarfEhFrameTest, Init32) {
this->eh_frame_->TestGetFdeInfo(0, &info);
EXPECT_EQ(0x5100U, info.offset);
EXPECT_EQ(0x660cU, info.start);
EXPECT_EQ(0x680cU, info.end);
EXPECT_EQ(0x6608U, info.start);
EXPECT_EQ(0x6808U, info.end);
this->eh_frame_->TestGetFdeInfo(1, &info);
EXPECT_EQ(0x5200U, info.offset);
EXPECT_EQ(0x770cU, info.start);
EXPECT_EQ(0x7a0cU, info.end);
EXPECT_EQ(0x7708U, info.start);
EXPECT_EQ(0x7a08U, info.end);
this->eh_frame_->TestGetFdeInfo(2, &info);
EXPECT_EQ(0x5400U, info.offset);
EXPECT_EQ(0x890cU, info.start);
EXPECT_EQ(0x8d0cU, info.end);
EXPECT_EQ(0x8908U, info.start);
EXPECT_EQ(0x8d08U, info.end);
this->eh_frame_->TestGetFdeInfo(3, &info);
EXPECT_EQ(0x5500U, info.offset);
EXPECT_EQ(0x9a0cU, info.start);
EXPECT_EQ(0x9f0cU, info.end);
EXPECT_EQ(0x9a08U, info.start);
EXPECT_EQ(0x9f08U, info.end);
}
TYPED_TEST_P(DwarfEhFrameTest, Init32_fde_not_following_cie) {
@ -193,23 +193,23 @@ TYPED_TEST_P(DwarfEhFrameTest, Init64) {
this->eh_frame_->TestGetFdeInfo(0, &info);
EXPECT_EQ(0x5100U, info.offset);
EXPECT_EQ(0x661cU, info.start);
EXPECT_EQ(0x681cU, info.end);
EXPECT_EQ(0x6618U, info.start);
EXPECT_EQ(0x6818U, info.end);
this->eh_frame_->TestGetFdeInfo(1, &info);
EXPECT_EQ(0x5200U, info.offset);
EXPECT_EQ(0x771cU, info.start);
EXPECT_EQ(0x7a1cU, info.end);
EXPECT_EQ(0x7718U, info.start);
EXPECT_EQ(0x7a18U, info.end);
this->eh_frame_->TestGetFdeInfo(2, &info);
EXPECT_EQ(0x5400U, info.offset);
EXPECT_EQ(0x891cU, info.start);
EXPECT_EQ(0x8d1cU, info.end);
EXPECT_EQ(0x8918U, info.start);
EXPECT_EQ(0x8d18U, info.end);
this->eh_frame_->TestGetFdeInfo(3, &info);
EXPECT_EQ(0x5500U, info.offset);
EXPECT_EQ(0x9a1cU, info.start);
EXPECT_EQ(0x9f1cU, info.end);
EXPECT_EQ(0x9a18U, info.start);
EXPECT_EQ(0x9f18U, info.end);
}
TYPED_TEST_P(DwarfEhFrameTest, Init64_fde_not_following_cie) {
@ -261,8 +261,8 @@ TYPED_TEST_P(DwarfEhFrameTest, Init_version1) {
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
this->eh_frame_->TestGetFdeInfo(0, &info);
EXPECT_EQ(0x5100U, info.offset);
EXPECT_EQ(0x660aU, info.start);
EXPECT_EQ(0x680aU, info.end);
EXPECT_EQ(0x6606U, info.start);
EXPECT_EQ(0x6806U, info.end);
}
TYPED_TEST_P(DwarfEhFrameTest, Init_version4) {
@ -304,8 +304,8 @@ TYPED_TEST_P(DwarfEhFrameTest, Init_version4) {
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
this->eh_frame_->TestGetFdeInfo(0, &info);
EXPECT_EQ(0x5100U, info.offset);
EXPECT_EQ(0x660aU, info.start);
EXPECT_EQ(0x680aU, info.end);
EXPECT_EQ(0x6606U, info.start);
EXPECT_EQ(0x6806U, info.end);
}
TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc) {
@ -384,8 +384,8 @@ TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) {
ASSERT_TRUE(fde != nullptr);
EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
EXPECT_EQ(0x1d00cU, fde->pc_start);
EXPECT_EQ(0x1d10cU, fde->pc_end);
EXPECT_EQ(0x1d008U, fde->pc_start);
EXPECT_EQ(0x1d108U, fde->pc_end);
EXPECT_EQ(0xf000U, fde->cie_offset);
EXPECT_EQ(0U, fde->lsda_address);
@ -428,8 +428,8 @@ TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
ASSERT_TRUE(fde != nullptr);
EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
EXPECT_EQ(0xd01cU, fde->pc_start);
EXPECT_EQ(0xd31cU, fde->pc_end);
EXPECT_EQ(0xd018U, fde->pc_start);
EXPECT_EQ(0xd318U, fde->pc_end);
EXPECT_EQ(0x6000U, fde->cie_offset);
EXPECT_EQ(0U, fde->lsda_address);

View file

@ -345,8 +345,8 @@ TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde32) {
ASSERT_TRUE(fde != nullptr);
EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
EXPECT_EQ(0x1d00cU, fde->pc_start);
EXPECT_EQ(0x1d10cU, fde->pc_end);
EXPECT_EQ(0x1d008U, fde->pc_start);
EXPECT_EQ(0x1d108U, fde->pc_end);
EXPECT_EQ(0xf000U, fde->cie_offset);
EXPECT_EQ(0U, fde->lsda_address);
@ -387,8 +387,8 @@ TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde64) {
ASSERT_TRUE(fde != nullptr);
EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
EXPECT_EQ(0xd01cU, fde->pc_start);
EXPECT_EQ(0xd31cU, fde->pc_end);
EXPECT_EQ(0xd018U, fde->pc_start);
EXPECT_EQ(0xd318U, fde->pc_end);
EXPECT_EQ(0x6000U, fde->cie_offset);
EXPECT_EQ(0U, fde->lsda_address);

View file

@ -132,7 +132,7 @@ TEST_F(ElfTest, elf_invalid) {
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
bool finished;
ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
ASSERT_FALSE(elf.Step(0, 0, 0, nullptr, nullptr, &finished));
}
TEST_F(ElfTest, elf32_invalid_machine) {
@ -306,7 +306,7 @@ TEST_F(ElfTest, step_in_signal_map) {
elf.FakeSetValid(true);
elf.FakeSetLoadBias(0);
bool finished;
ASSERT_TRUE(elf.Step(0x1000, 0x2000, &regs, &process_memory, &finished));
ASSERT_TRUE(elf.Step(0x1000, 0x1000, 0x2000, &regs, &process_memory, &finished));
EXPECT_FALSE(finished);
EXPECT_EQ(15U, regs.pc());
EXPECT_EQ(13U, regs.sp());
@ -339,7 +339,7 @@ TEST_F(ElfTest, step_in_interface) {
EXPECT_CALL(*interface, Step(0x1000, &regs, &process_memory, &finished))
.WillOnce(::testing::Return(true));
ASSERT_TRUE(elf.Step(0x1000, 0x2000, &regs, &process_memory, &finished));
ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, &regs, &process_memory, &finished));
}
TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
@ -355,12 +355,12 @@ TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
// Invalid relative pc given load_bias.
bool finished;
ASSERT_FALSE(elf.Step(0x1000, 0x2000, &regs, &process_memory, &finished));
ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, &regs, &process_memory, &finished));
EXPECT_CALL(*interface, Step(0x3300, &regs, &process_memory, &finished))
.WillOnce(::testing::Return(true));
ASSERT_TRUE(elf.Step(0x7300, 0x2000, &regs, &process_memory, &finished));
ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, &regs, &process_memory, &finished));
}
} // namespace unwindstack

View file

@ -47,7 +47,7 @@ void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type) {
ehdr->e_ehsize = sizeof(Ehdr);
}
static std::string GetTestFileDirectory() {
std::string TestGetFileDirectory() {
std::string exec(testing::internal::GetArgvs()[0]);
auto const value = exec.find_last_of('/');
if (value == std::string::npos) {
@ -102,7 +102,7 @@ void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine, bool init_gnu_de
offset = symtab_offset + 0x100;
if (init_gnu_debugdata) {
// Read in the compressed elf data and copy it in.
name = GetTestFileDirectory();
name = TestGetFileDirectory();
if (elf_class == ELFCLASS32) {
name += "elf32.xz";
} else {

View file

@ -18,6 +18,7 @@
#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
#include <functional>
#include <string>
namespace unwindstack {
@ -30,6 +31,8 @@ template <typename Ehdr, typename Shdr>
void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata,
TestCopyFuncType copy_func);
std::string TestGetFileDirectory();
} // namespace unwindstack
#endif // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H

View file

@ -0,0 +1,153 @@
/*
* 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 <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <gtest/gtest.h>
#include <string>
#include <vector>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/Unwinder.h>
#include "Machine.h"
#include "ElfTestUtils.h"
namespace unwindstack {
static std::string DumpFrames(Unwinder& unwinder) {
std::string str;
for (size_t i = 0; i < unwinder.NumFrames(); i++) {
str += unwinder.FormatFrame(i) + "\n";
}
return str;
}
TEST(UnwindOfflineTest, pc_straddle_arm32) {
std::string dir(TestGetFileDirectory() + "offline/straddle_arm32/");
MemoryOffline* memory = new MemoryOffline;
ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
ASSERT_TRUE(fp != nullptr);
RegsArm regs;
uint64_t reg_value;
ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
regs[ARM_REG_PC] = reg_value;
ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
regs[ARM_REG_SP] = reg_value;
ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
regs[ARM_REG_LR] = reg_value;
regs.SetFromRaw();
fclose(fp);
fp = fopen((dir + "maps.txt").c_str(), "r");
ASSERT_TRUE(fp != nullptr);
// The file is guaranteed to be less than 4096 bytes.
std::vector<char> buffer(4096);
ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
fclose(fp);
BufferMaps maps(buffer.data());
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(static_cast<uint32_t>(EM_ARM), regs.MachineType());
std::shared_ptr<Memory> process_memory(memory);
char* cwd = getcwd(nullptr, 0);
ASSERT_EQ(0, chdir(dir.c_str()));
Unwinder unwinder(128, &maps, &regs, process_memory);
unwinder.Unwind();
ASSERT_EQ(0, chdir(cwd));
free(cwd);
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 0001a9f8 libc.so (abort+63)\n"
" #01 pc 00006a1b libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
" #02 pc 00007441 libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
" #03 pc 00015149 /does/not/exist/libhidlbase.so\n",
frame_info);
}
TEST(UnwindOfflineTest, pc_straddle_arm64) {
std::string dir(TestGetFileDirectory() + "offline/straddle_arm64/");
MemoryOffline* memory = new MemoryOffline;
ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
ASSERT_TRUE(fp != nullptr);
RegsArm64 regs;
uint64_t reg_value;
ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
regs[ARM64_REG_PC] = reg_value;
ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
regs[ARM64_REG_SP] = reg_value;
ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
regs[ARM64_REG_LR] = reg_value;
ASSERT_EQ(1, fscanf(fp, "x29: %" SCNx64 "\n", &reg_value));
regs[ARM64_REG_R29] = reg_value;
regs.SetFromRaw();
fclose(fp);
fp = fopen((dir + "maps.txt").c_str(), "r");
ASSERT_TRUE(fp != nullptr);
// The file is guaranteed to be less than 4096 bytes.
std::vector<char> buffer(4096);
ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
fclose(fp);
BufferMaps maps(buffer.data());
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(static_cast<uint32_t>(EM_AARCH64), regs.MachineType());
std::shared_ptr<Memory> process_memory(memory);
char* cwd = getcwd(nullptr, 0);
ASSERT_EQ(0, chdir(dir.c_str()));
Unwinder unwinder(128, &maps, &regs, process_memory);
unwinder.Unwind();
ASSERT_EQ(0, chdir(cwd));
free(cwd);
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 0000000000429fd8 libunwindstack_test (SignalInnerFunction+24)\n"
" #01 pc 000000000042a078 libunwindstack_test (SignalMiddleFunction+8)\n"
" #02 pc 000000000042a08c libunwindstack_test (SignalOuterFunction+8)\n"
" #03 pc 000000000042d8fc libunwindstack_test "
"(_ZN11unwindstackL19RemoteThroughSignalEij+20)\n"
" #04 pc 000000000042d8d8 libunwindstack_test "
"(_ZN11unwindstack37UnwindTest_remote_through_signal_Test8TestBodyEv+32)\n"
" #05 pc 0000000000455d70 libunwindstack_test (_ZN7testing4Test3RunEv+392)\n",
frame_info);
}
} // namespace unwindstack

View file

@ -0,0 +1,4 @@
f2d9a000-f2da7fff r-xp 00000000 00:00 0 libbase.so
f3002000-f3005fff rw-p 00000000 00:00 0 [stack:25941]
f31d0000-f326bfff r-xp 00000000 00:00 0 libc.so
f3352000-f336bfff r-xp 00000000 00:00 0 /does/not/exist/libhidlbase.so

View file

@ -0,0 +1,3 @@
pc: f31ea9f8
sp: e9c866f8
lr: f31f179f

View file

@ -0,0 +1,2 @@
00000064d05ab000-00000064d0a6cfff r-xp 00000000 00:00 0 libunwindstack_test
0000007fe0d64000-0000007fe0d84fff rw-p 00000000 00:00 0 [stack]

View file

@ -0,0 +1,4 @@
pc: 00000064d09d4fd8
sp: 0000007fe0d84040
lr: 00000064d09d507c
x29: 0000007fe0d84070