Add ability to read jit gdb data.
Changes:
- New JitDebug class to handle all of the jit gdb interface.
- Add unit tests for all, along with new offline test using debug data.
- Add new Memory type called MemoryOfflineParts that has multiple
MemoryOffline objects to support the offline test.
- Update the tools to use the JitDebug object.
- Modify libbacktrace to use the JitDebug, but only looking in libart.so
and libartd.so.
- Change the Format32Bits to Is32Bit since it's more accurate and I use
it in a different context where original name didn't make sense.
- Add a new function to find global variables in an elf file
(GetGlobalVariable).
- Add a new function to determine if a pc is valid for this elf (IsValidPc).
Bug: 68396769
Test: Ran new unit tests. Added new offline test that uses jit debug data.
Test: Ran art test that generates jit data and verified a crash unwinds
Test: through the jit data.
Change-Id: I6e7ee2f5bab2242028a06feece156dff21c0a974
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index 68de797..ae9da5e 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -43,6 +43,15 @@
return true;
}
+bool ElfInterfaceFake::GetGlobalVariable(const std::string& global, uint64_t* offset) {
+ auto entry = globals_.find(global);
+ if (entry == globals_.end()) {
+ return false;
+ }
+ *offset = entry->second;
+ return true;
+}
+
bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) {
if (steps_.empty()) {
return false;
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index abf9927..099026c 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -21,6 +21,7 @@
#include <deque>
#include <string>
+#include <unordered_map>
#include <unwindstack/Elf.h>
#include <unwindstack/ElfInterface.h>
@@ -55,6 +56,9 @@
void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
+ void FakeSetGnuDebugdataInterface(ElfInterface* interface) {
+ gnu_debugdata_interface_.reset(interface);
+ }
};
class ElfInterfaceFake : public ElfInterface {
@@ -67,9 +71,14 @@
bool GetSoname(std::string*) override { return false; }
bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
+ bool GetGlobalVariable(const std::string&, uint64_t*) override;
bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override;
+ void FakeSetGlobalVariable(const std::string& global, uint64_t offset) {
+ globals_[global] = offset;
+ }
+
static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
static void FakePushStepData(const StepData data) { steps_.push_back(data); }
@@ -79,6 +88,8 @@
}
private:
+ std::unordered_map<std::string, uint64_t> globals_;
+
static std::deque<FunctionData> functions_;
static std::deque<StepData> steps_;
};
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index e138c3a..042c5fb 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -958,4 +958,189 @@
InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
}
+TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+ Elf32_Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 1;
+ ehdr.e_phentsize = sizeof(Elf32_Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Phdr phdr;
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0U, load_bias);
+ EXPECT_TRUE(elf->IsValidPc(0));
+ EXPECT_TRUE(elf->IsValidPc(0x5000));
+ EXPECT_TRUE(elf->IsValidPc(0xffff));
+ EXPECT_FALSE(elf->IsValidPc(0x10000));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load_non_zero_load_bias) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+ Elf32_Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 1;
+ ehdr.e_phentsize = sizeof(Elf32_Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Phdr phdr;
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0x2000;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0x2000U, load_bias);
+ EXPECT_FALSE(elf->IsValidPc(0));
+ EXPECT_FALSE(elf->IsValidPc(0x1000));
+ EXPECT_FALSE(elf->IsValidPc(0x1fff));
+ EXPECT_TRUE(elf->IsValidPc(0x2000));
+ EXPECT_TRUE(elf->IsValidPc(0x5000));
+ EXPECT_TRUE(elf->IsValidPc(0x11fff));
+ EXPECT_FALSE(elf->IsValidPc(0x12000));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_debug_frame) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+ uint64_t sh_offset = 0x100;
+
+ Elf32_Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr);
+ ehdr.e_shnum = 3;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x500;
+ shdr.sh_size = 0x100;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+ memory_.SetMemory(0x500, ".debug_frame");
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0;
+ shdr.sh_addr = 0x600;
+ shdr.sh_offset = 0x600;
+ shdr.sh_size = 0x200;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+ // CIE 32.
+ memory_.SetData32(0x600, 0xfc);
+ memory_.SetData32(0x604, 0xffffffff);
+ memory_.SetData8(0x608, 1);
+ memory_.SetData8(0x609, '\0');
+ memory_.SetData8(0x60a, 0x4);
+ memory_.SetData8(0x60b, 0x4);
+ memory_.SetData8(0x60c, 0x1);
+
+ // FDE 32.
+ memory_.SetData32(0x700, 0xfc);
+ memory_.SetData32(0x704, 0);
+ memory_.SetData32(0x708, 0x2100);
+ memory_.SetData32(0x70c, 0x200);
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ elf->InitHeaders();
+ EXPECT_EQ(0U, load_bias);
+ EXPECT_FALSE(elf->IsValidPc(0));
+ EXPECT_FALSE(elf->IsValidPc(0x20ff));
+ EXPECT_TRUE(elf->IsValidPc(0x2100));
+ EXPECT_TRUE(elf->IsValidPc(0x2200));
+ EXPECT_TRUE(elf->IsValidPc(0x22ff));
+ EXPECT_FALSE(elf->IsValidPc(0x2300));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_eh_frame) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+ uint64_t sh_offset = 0x100;
+
+ Elf32_Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr);
+ ehdr.e_shnum = 3;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x500;
+ shdr.sh_size = 0x100;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+ memory_.SetMemory(0x500, ".eh_frame");
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0;
+ shdr.sh_addr = 0x600;
+ shdr.sh_offset = 0x600;
+ shdr.sh_size = 0x200;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+ // CIE 32.
+ memory_.SetData32(0x600, 0xfc);
+ memory_.SetData32(0x604, 0);
+ memory_.SetData8(0x608, 1);
+ memory_.SetData8(0x609, '\0');
+ memory_.SetData8(0x60a, 0x4);
+ memory_.SetData8(0x60b, 0x4);
+ memory_.SetData8(0x60c, 0x1);
+
+ // FDE 32.
+ memory_.SetData32(0x700, 0xfc);
+ memory_.SetData32(0x704, 0x104);
+ memory_.SetData32(0x708, 0x20f8);
+ memory_.SetData32(0x70c, 0x200);
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ elf->InitHeaders();
+ EXPECT_EQ(0U, load_bias);
+ EXPECT_FALSE(elf->IsValidPc(0));
+ EXPECT_FALSE(elf->IsValidPc(0x27ff));
+ EXPECT_TRUE(elf->IsValidPc(0x2800));
+ EXPECT_TRUE(elf->IsValidPc(0x2900));
+ EXPECT_TRUE(elf->IsValidPc(0x29ff));
+ EXPECT_FALSE(elf->IsValidPc(0x2a00));
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 5e808ef..7e6a62a 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -346,7 +346,14 @@
void InitHeaders() override {}
bool GetSoname(std::string*) override { return false; }
bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
+
MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*));
+ MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
+ MOCK_METHOD1(IsValidPc, bool(uint64_t));
+
+ void MockSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; }
+ void MockSetDynamicVaddr(uint64_t vaddr) { dynamic_vaddr_ = vaddr; }
+ void MockSetDynamicSize(uint64_t size) { dynamic_size_ = size; }
};
TEST_F(ElfTest, step_in_interface) {
@@ -378,14 +385,200 @@
elf.FakeSetInterface(interface);
MemoryFake process_memory;
- // Invalid relative pc given load_bias.
bool finished;
- ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
-
EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished));
}
+TEST_F(ElfTest, get_global_invalid_elf) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(false);
+
+ std::string global("something");
+ uint64_t offset;
+ ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_not_in_interface) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false));
+
+ ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_below_load_bias) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0x1000);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+ ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_zero) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetGnuDebugdataInterface(gnu_interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false));
+
+ EXPECT_CALL(*gnu_interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x500), ::testing::Return(true)));
+
+ ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+ EXPECT_EQ(0x500U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_in_gnu_debugdata_dynamic_zero) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+ ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+ EXPECT_EQ(0x300U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0x100);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+ ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+ EXPECT_EQ(0x200U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_adjust_negative) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ interface->MockSetDynamicOffset(0x400);
+ interface->MockSetDynamicVaddr(0x800);
+ interface->MockSetDynamicSize(0x100);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true)));
+
+ ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+ EXPECT_EQ(0x450U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_adjust_positive) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ interface->MockSetDynamicOffset(0x1000);
+ interface->MockSetDynamicVaddr(0x800);
+ interface->MockSetDynamicSize(0x100);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true)));
+
+ ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+ EXPECT_EQ(0x1050U, offset);
+}
+
+TEST_F(ElfTest, is_valid_pc_elf_invalid) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(false);
+ elf.FakeSetLoadBias(0);
+
+ EXPECT_FALSE(elf.IsValidPc(0x100));
+ EXPECT_FALSE(elf.IsValidPc(0x200));
+}
+
+TEST_F(ElfTest, is_valid_pc_interface) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true));
+
+ EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
+TEST_F(ElfTest, is_valid_pc_non_zero_load_bias) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0x1000);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ EXPECT_CALL(*interface, IsValidPc(0x500)).WillOnce(::testing::Return(true));
+
+ EXPECT_FALSE(elf.IsValidPc(0x100));
+ EXPECT_FALSE(elf.IsValidPc(0x200));
+ EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
+TEST_F(ElfTest, is_valid_pc_from_gnu_debugdata) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+ ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetGnuDebugdataInterface(gnu_interface);
+
+ EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*gnu_interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true));
+
+ EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
new file mode 100644
index 0000000..83d7a49
--- /dev/null
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2018 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 <string.h>
+
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class JitDebugTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+
+ jit_debug_.reset(new JitDebug(process_memory_));
+ jit_debug_->SetArch(ARCH_ARM);
+
+ maps_.reset(
+ new BufferMaps("1000-4000 ---s 00000000 00:00 0\n"
+ "4000-6000 r--s 00000000 00:00 0\n"
+ "6000-8000 -w-s 00000000 00:00 0\n"
+ "a000-c000 --xp 00000000 00:00 0\n"
+ "c000-f000 rwxp 00000000 00:00 0\n"
+ "f000-11000 r-xp 00000000 00:00 0\n"
+ "100000-110000 rw-p 0000000 00:00 0\n"
+ "200000-210000 rw-p 0000000 00:00 0\n"));
+ ASSERT_TRUE(maps_->Parse());
+
+ MapInfo* map_info = maps_->Get(3);
+ ASSERT_TRUE(map_info != nullptr);
+ elf_memories_.push_back(new MemoryFake);
+ ElfFake* elf = new ElfFake(elf_memories_.back());
+ elf->FakeSetValid(true);
+ ElfInterfaceFake* interface = new ElfInterfaceFake(elf_memories_.back());
+ elf->FakeSetInterface(interface);
+ interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+ map_info->elf = elf;
+
+ map_info = maps_->Get(5);
+ ASSERT_TRUE(map_info != nullptr);
+ elf_memories_.push_back(new MemoryFake);
+ elf = new ElfFake(elf_memories_.back());
+ elf->FakeSetValid(true);
+ interface = new ElfInterfaceFake(elf_memories_.back());
+ elf->FakeSetInterface(interface);
+ interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+ map_info->elf = elf;
+ }
+
+ template <typename EhdrType, typename ShdrType>
+ void CreateElf(uint64_t offset, uint8_t class_type, uint8_t machine_type, uint32_t pc,
+ uint32_t size) {
+ EhdrType ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ uint64_t sh_offset = sizeof(ehdr);
+ memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+ ehdr.e_ident[EI_CLASS] = class_type;
+ ehdr.e_machine = machine_type;
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(ShdrType);
+ ehdr.e_shnum = 3;
+ memory_->SetMemory(offset, &ehdr, sizeof(ehdr));
+
+ ShdrType shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x500;
+ shdr.sh_size = 0x100;
+ memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+ memory_->SetMemory(offset + 0x500, ".debug_frame");
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0;
+ shdr.sh_addr = 0x600;
+ shdr.sh_offset = 0x600;
+ shdr.sh_size = 0x200;
+ memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+ // Now add a single cie/fde.
+ uint64_t dwarf_offset = offset + 0x600;
+ if (class_type == ELFCLASS32) {
+ // CIE 32 information.
+ memory_->SetData32(dwarf_offset, 0xfc);
+ memory_->SetData32(dwarf_offset + 0x4, 0xffffffff);
+ memory_->SetData8(dwarf_offset + 0x8, 1);
+ memory_->SetData8(dwarf_offset + 0x9, '\0');
+ memory_->SetData8(dwarf_offset + 0xa, 0x4);
+ memory_->SetData8(dwarf_offset + 0xb, 0x4);
+ memory_->SetData8(dwarf_offset + 0xc, 0x1);
+
+ // FDE 32 information.
+ memory_->SetData32(dwarf_offset + 0x100, 0xfc);
+ memory_->SetData32(dwarf_offset + 0x104, 0);
+ memory_->SetData32(dwarf_offset + 0x108, pc);
+ memory_->SetData32(dwarf_offset + 0x10c, size);
+ } else {
+ // CIE 64 information.
+ memory_->SetData32(dwarf_offset, 0xffffffff);
+ memory_->SetData64(dwarf_offset + 4, 0xf4);
+ memory_->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL);
+ memory_->SetData8(dwarf_offset + 0x14, 1);
+ memory_->SetData8(dwarf_offset + 0x15, '\0');
+ memory_->SetData8(dwarf_offset + 0x16, 0x4);
+ memory_->SetData8(dwarf_offset + 0x17, 0x4);
+ memory_->SetData8(dwarf_offset + 0x18, 0x1);
+
+ // FDE 64 information.
+ memory_->SetData32(dwarf_offset + 0x100, 0xffffffff);
+ memory_->SetData64(dwarf_offset + 0x104, 0xf4);
+ memory_->SetData64(dwarf_offset + 0x10c, 0);
+ memory_->SetData64(dwarf_offset + 0x114, pc);
+ memory_->SetData64(dwarf_offset + 0x11c, size);
+ }
+ }
+
+ void WriteDescriptor32(uint64_t addr, uint32_t entry);
+ void WriteDescriptor64(uint64_t addr, uint64_t entry);
+ void WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+ uint64_t elf_size);
+ void WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+ uint64_t elf_size);
+ void WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr,
+ uint64_t elf_size);
+
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_;
+ std::vector<MemoryFake*> elf_memories_;
+ std::unique_ptr<JitDebug> jit_debug_;
+ std::unique_ptr<BufferMaps> maps_;
+};
+
+void JitDebugTest::WriteDescriptor32(uint64_t addr, uint32_t entry) {
+ // Format of the 32 bit JITDescriptor structure:
+ // uint32_t version
+ memory_->SetData32(addr, 1);
+ // uint32_t action_flag
+ memory_->SetData32(addr + 4, 0);
+ // uint32_t relevant_entry
+ memory_->SetData32(addr + 8, 0);
+ // uint32_t first_entry
+ memory_->SetData32(addr + 12, entry);
+}
+
+void JitDebugTest::WriteDescriptor64(uint64_t addr, uint64_t entry) {
+ // Format of the 64 bit JITDescriptor structure:
+ // uint32_t version
+ memory_->SetData32(addr, 1);
+ // uint32_t action_flag
+ memory_->SetData32(addr + 4, 0);
+ // uint64_t relevant_entry
+ memory_->SetData64(addr + 8, 0);
+ // uint64_t first_entry
+ memory_->SetData64(addr + 16, entry);
+}
+
+void JitDebugTest::WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+ uint64_t elf_size) {
+ // Format of the 32 bit JITCodeEntry structure:
+ // uint32_t next
+ memory_->SetData32(addr, next);
+ // uint32_t prev
+ memory_->SetData32(addr + 4, prev);
+ // uint32_t symfile_addr
+ memory_->SetData32(addr + 8, elf_addr);
+ // uint64_t symfile_size
+ memory_->SetData64(addr + 12, elf_size);
+}
+
+void JitDebugTest::WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+ uint64_t elf_size) {
+ // Format of the 32 bit JITCodeEntry structure:
+ // uint32_t next
+ memory_->SetData32(addr, next);
+ // uint32_t prev
+ memory_->SetData32(addr + 4, prev);
+ // uint32_t symfile_addr
+ memory_->SetData32(addr + 8, elf_addr);
+ // uint32_t pad
+ memory_->SetData32(addr + 12, 0);
+ // uint64_t symfile_size
+ memory_->SetData64(addr + 16, elf_size);
+}
+
+void JitDebugTest::WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr,
+ uint64_t elf_size) {
+ // Format of the 64 bit JITCodeEntry structure:
+ // uint64_t next
+ memory_->SetData64(addr, next);
+ // uint64_t prev
+ memory_->SetData64(addr + 8, prev);
+ // uint64_t symfile_addr
+ memory_->SetData64(addr + 16, elf_addr);
+ // uint64_t symfile_size
+ memory_->SetData64(addr + 24, elf_size);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid) {
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_global_variable) {
+ maps_.reset(new BufferMaps(""));
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_valid_descriptor_in_memory) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_valid_code_entry) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0xf800, 0x200000);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid_descriptor_first_entry) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0xf800, 0);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid_descriptor_version) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0xf800, 0x20000);
+ // Set the version to an invalid value.
+ memory_->SetData32(0xf800, 2);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_32) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0xf800, 0x200000);
+ WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf != nullptr);
+
+ // Clear the memory and verify all of the data is cached.
+ memory_->Clear();
+ Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf2 != nullptr);
+ EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_elf_x86) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0xf800, 0x200000);
+ WriteEntry32Pack(0x200000, 0, 0, 0x4000, 0x1000);
+
+ jit_debug_->SetArch(ARCH_X86);
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf != nullptr);
+
+ // Clear the memory and verify all of the data is cached.
+ memory_->Clear();
+ Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf2 != nullptr);
+ EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_elf_64) {
+ CreateElf<Elf64_Ehdr, Elf64_Shdr>(0x4000, ELFCLASS64, EM_AARCH64, 0x1500, 0x200);
+
+ WriteDescriptor64(0xf800, 0x200000);
+ WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
+
+ jit_debug_->SetArch(ARCH_ARM64);
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf != nullptr);
+
+ // Clear the memory and verify all of the data is cached.
+ memory_->Clear();
+ Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf2 != nullptr);
+ EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_elf_multiple_entries) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x5000, ELFCLASS32, EM_ARM, 0x2300, 0x400);
+
+ WriteDescriptor32(0xf800, 0x200000);
+ WriteEntry32Pad(0x200000, 0, 0x200100, 0x4000, 0x1000);
+ WriteEntry32Pad(0x200100, 0x200100, 0, 0x5000, 0x1000);
+
+ Elf* elf_2 = jit_debug_->GetElf(maps_.get(), 0x2400);
+ ASSERT_TRUE(elf_2 != nullptr);
+
+ Elf* elf_1 = jit_debug_->GetElf(maps_.get(), 0x1600);
+ ASSERT_TRUE(elf_1 != nullptr);
+
+ // Clear the memory and verify all of the data is cached.
+ memory_->Clear();
+ EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x1500));
+ EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x16ff));
+ EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x2300));
+ EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x26ff));
+ EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x1700));
+ EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x2700));
+}
+
+TEST_F(JitDebugTest, get_elf_search_libs) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0xf800, 0x200000);
+ WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+
+ // Only search a given named list of libs.
+ std::vector<std::string> libs{"libart.so"};
+ jit_debug_.reset(new JitDebug(process_memory_, libs));
+ jit_debug_->SetArch(ARCH_ARM);
+ EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
+
+ // Change the name of the map that includes the value and verify this works.
+ MapInfo* map_info = maps_->Get(5);
+ map_info->name = "/system/lib/libart.so";
+ jit_debug_.reset(new JitDebug(process_memory_, libs));
+ // Make sure that clearing our copy of the libs doesn't affect the
+ // JitDebug object.
+ libs.clear();
+ jit_debug_->SetArch(ARCH_ARM);
+ EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index b81b2ca..8f7d913 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -45,7 +45,7 @@
void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
- bool Format32Bit() { return false; }
+ bool Is32Bit() { return false; }
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; }
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 3f84890..15d5458 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -188,7 +188,7 @@
regs_arm.set_pc(0x1500);
EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
- EXPECT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, invalid_elf));
+ EXPECT_EQ(0x4fcU, regs_arm.GetAdjustedPc(0x500U, invalid_elf));
regs_arm64.set_pc(0x1600);
EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index da258a6..45a7b58 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -330,9 +330,69 @@
ASSERT_EQ(3U, func_offset);
}
+TYPED_TEST_P(SymbolsTest, get_global) {
+ uint64_t start_offset = 0x1000;
+ uint64_t str_offset = 0xa000;
+ Symbols symbols(start_offset, 4 * sizeof(TypeParam), sizeof(TypeParam), str_offset, 0x1000);
+
+ TypeParam sym;
+ memset(&sym, 0, sizeof(sym));
+ sym.st_shndx = SHN_COMMON;
+ sym.st_info = STT_OBJECT | (STB_GLOBAL << 4);
+ sym.st_name = 0x100;
+ this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+ this->memory_.SetMemory(str_offset + 0x100, "global_0");
+
+ start_offset += sizeof(sym);
+ memset(&sym, 0, sizeof(sym));
+ sym.st_shndx = SHN_COMMON;
+ sym.st_info = STT_FUNC;
+ sym.st_name = 0x200;
+ sym.st_value = 0x10000;
+ sym.st_size = 0x100;
+ this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+ this->memory_.SetMemory(str_offset + 0x200, "function_0");
+
+ start_offset += sizeof(sym);
+ memset(&sym, 0, sizeof(sym));
+ sym.st_shndx = SHN_COMMON;
+ sym.st_info = STT_OBJECT | (STB_GLOBAL << 4);
+ sym.st_name = 0x300;
+ this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+ this->memory_.SetMemory(str_offset + 0x300, "global_1");
+
+ start_offset += sizeof(sym);
+ memset(&sym, 0, sizeof(sym));
+ sym.st_shndx = SHN_COMMON;
+ sym.st_info = STT_FUNC;
+ sym.st_name = 0x400;
+ sym.st_value = 0x12000;
+ sym.st_size = 0x100;
+ this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+ this->memory_.SetMemory(str_offset + 0x400, "function_1");
+
+ uint64_t offset;
+ EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
+ EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
+ EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
+ EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
+
+ EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_0", &offset));
+ EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_1", &offset));
+
+ std::string name;
+ EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, 0, &this->memory_, &name, &offset));
+ EXPECT_EQ("function_0", name);
+ EXPECT_EQ(2U, offset);
+
+ EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, 0, &this->memory_, &name, &offset));
+ EXPECT_EQ("function_1", name);
+ EXPECT_EQ(4U, offset);
+}
+
REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds,
- symtab_read_cached);
+ symtab_read_cached, get_global);
typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 8f28036..9216204 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -25,14 +25,17 @@
#include <string>
#include <vector>
+#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/RegsArm.h>
#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
#include <unwindstack/Unwinder.h>
#include "MachineArm.h"
#include "MachineArm64.h"
+#include "MachineX86.h"
#include "ElfTestUtils.h"
@@ -92,7 +95,7 @@
" #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",
+ " #03 pc 00015147 /does/not/exist/libhidlbase.so\n",
frame_info);
}
@@ -200,4 +203,207 @@
frame_info);
}
+static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
+ MemoryOffline* memory = new MemoryOffline;
+ ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
+ parts->Add(memory);
+}
+
+TEST(UnwindOfflineTest, jit_debug_x86_32) {
+ std::string dir(TestGetFileDirectory() + "offline/jit_debug_x86_32/");
+
+ MemoryOfflineParts* memory = new MemoryOfflineParts;
+ AddMemory(dir + "descriptor.data", memory);
+ AddMemory(dir + "stack.data", memory);
+ for (size_t i = 0; i < 7; i++) {
+ AddMemory(dir + "entry" + std::to_string(i) + ".data", memory);
+ AddMemory(dir + "jit" + std::to_string(i) + ".data", memory);
+ }
+
+ FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
+ ASSERT_TRUE(fp != nullptr);
+ RegsX86 regs;
+ uint64_t reg_value;
+ ASSERT_EQ(1, fscanf(fp, "eax: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_EAX] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "ebx: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_EBX] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "ecx: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_ECX] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "edx: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_EDX] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "ebp: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_EBP] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "edi: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_EDI] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "esi: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_ESI] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "esp: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_ESP] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "eip: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_EIP] = 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(ARCH_X86, regs.Arch());
+
+ std::shared_ptr<Memory> process_memory(memory);
+
+ char* cwd = getcwd(nullptr, 0);
+ ASSERT_EQ(0, chdir(dir.c_str()));
+ JitDebug jit_debug(process_memory);
+ Unwinder unwinder(128, &maps, ®s, process_memory);
+ unwinder.SetJitDebug(&jit_debug, regs.Arch());
+ unwinder.Unwind();
+ ASSERT_EQ(0, chdir(cwd));
+ free(cwd);
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00068fb8 libarttestd.so (_ZN3artL13CauseSegfaultEv+72)\n"
+ " #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n"
+ " #02 pc 000021a8 (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
+ "boolean)+136)\n"
+ " #03 pc 0000fe81 anonymous:ee74c000 (boolean Main.bar(boolean)+65)\n"
+ " #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #05 pc 00146ab5 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ " #06 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #07 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #08 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #09 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #10 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #11 pc 0000fe04 anonymous:ee74c000 (int Main.compare(Main, Main)+52)\n"
+ " #12 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #13 pc 00146ab5 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ " #14 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #15 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #16 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #17 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #18 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #19 pc 0000fd3c anonymous:ee74c000 (int Main.compare(java.lang.Object, "
+ "java.lang.Object)+108)\n"
+ " #20 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #21 pc 00146ab5 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ " #22 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #23 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #24 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #25 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #26 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #27 pc 0000fbdc anonymous:ee74c000 (int "
+ "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
+ "java.util.Comparator)+332)\n"
+ " #28 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
+ " #29 pc 00146acb libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+ " #30 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #31 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #32 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #33 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #34 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #35 pc 0000f625 anonymous:ee74c000 (boolean Main.foo()+165)\n"
+ " #36 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #37 pc 00146ab5 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ " #38 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #39 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #40 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #41 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #42 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #43 pc 0000eedc anonymous:ee74c000 (void Main.runPrimary()+60)\n"
+ " #44 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #45 pc 00146ab5 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ " #46 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #47 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #48 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #49 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #50 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #51 pc 0000ac22 anonymous:ee74c000 (void Main.main(java.lang.String[])+98)\n"
+ " #52 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
+ " #53 pc 00146acb libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+ " #54 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #55 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #56 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #57 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #58 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #59 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
+ " #60 pc 00146acb libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+ " #61 pc 005aac95 libartd.so "
+ "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
+ "8ArgArrayEPNS_6JValueEPKc+85)\n"
+ " #62 pc 005aab5a libartd.so "
+ "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
+ "jmethodIDPc+362)\n"
+ " #63 pc 0048a3dd libartd.so "
+ "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+125)\n"
+ " #64 pc 0018448c libartd.so "
+ "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDPcNS_"
+ "9Primitive4TypeENS_10InvokeTypeE+1964)\n"
+ " #65 pc 0017cf06 libartd.so "
+ "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+70)\n"
+ " #66 pc 00001d8c dalvikvm32 "
+ "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+60)\n"
+ " #67 pc 00001a80 dalvikvm32 (main+1312)\n"
+ " #68 pc 00018275 libc.so\n",
+ frame_info);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex b/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex
new file mode 100644
index 0000000..870ac0a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32
new file mode 100644
index 0000000..76ffad9
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data
new file mode 100644
index 0000000..466dae2
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data
new file mode 100644
index 0000000..3a725e8
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data
new file mode 100644
index 0000000..767550f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data
new file mode 100644
index 0000000..e7e492e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data
new file mode 100644
index 0000000..65f9cd4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data
new file mode 100644
index 0000000..30aa28c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data
new file mode 100644
index 0000000..3c89673
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data
new file mode 100644
index 0000000..9c9b83c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data
new file mode 100644
index 0000000..eaad142
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data
new file mode 100644
index 0000000..d534816
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data
new file mode 100644
index 0000000..dbeb886
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data
new file mode 100644
index 0000000..bf2142d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data
new file mode 100644
index 0000000..e2ba1b0
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data
new file mode 100644
index 0000000..c27ba54
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data
new file mode 100644
index 0000000..5fc8fae
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so
new file mode 100644
index 0000000..70352db
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so
new file mode 100644
index 0000000..e72e673
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so
new file mode 100644
index 0000000..9c78790
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt
new file mode 100644
index 0000000..db4f9f7
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt
@@ -0,0 +1,6 @@
+56573000-56577000 r-xp 0 00:00 0 dalvikvm32
+eb833000-eb8cc000 r-xp 0 00:00 0 libarttestd.so
+ec606000-ec607000 r-xp 2000 00:00 0 137-cfi.odex
+ee74c000-f074c000 r-xp 0 00:00 0 anonymous:ee74c000
+f6be1000-f732b000 r-xp 0 00:00 0 libartd.so
+f734b000-f74fc000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt b/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt
new file mode 100644
index 0000000..f68305b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt
@@ -0,0 +1,9 @@
+eax: eb8cccd0
+ebx: eb8cccd0
+ecx: ff
+edx: ffeb2ca8
+ebp: ffeb5298
+edi: ffeb5c08
+esi: ffeb5c00
+esp: ffeb5280
+eip: eb89bfb8
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data
new file mode 100644
index 0000000..c345762
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data
Binary files differ