Add a first unit test for the arm64 instruction profiled unwind

plan generator.

Fix a small bug in EmulateInstructionARM64::GetFramePointerRegister
which was returning the stack pointer reg instead of fp, prevented
the unwinder from recognizing the switch to using the fp in a
function. (<rdar://problem/28663117>)

Add a new eContextRestoreStackPointer context hint so that the arm64
emulator can flag when the frame pointer value is copied back in to
the stack pointer and that should be used to compute the canonical
frame address again in an epilogue sequence.  (<rdar://problem/28704862>)

Small changes to UnwindAssemblyInstEmulation to have a method we can
call without a live process/thread/etc for unit tests.

<rdar://problem/28663117> 
<rdar://problem/28704862> 
<rdar://problem/28509178> 

llvm-svn: 283847
This commit is contained in:
Jason Molenda 2016-10-11 02:24:00 +00:00
parent 8e7fe2c741
commit 6853cca1c9
8 changed files with 241 additions and 17 deletions

View File

@ -111,6 +111,10 @@ public:
// Adjust the frame pointer for the current frame // Adjust the frame pointer for the current frame
eContextSetFramePointer, eContextSetFramePointer,
// Typically in an epilogue sequence. Copy the frame pointer back
// into the stack pointer, use SP for CFA calculations again.
eContextRestoreStackPointer,
// Add or subtract a value from a base address register (other than SP) // Add or subtract a value from a base address register (other than SP)
eContextAdjustBaseRegister, eContextAdjustBaseRegister,

View File

@ -916,6 +916,7 @@
AF20F76A1AF18F9000751A6E /* ABISysV_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF20F7681AF18F9000751A6E /* ABISysV_arm64.cpp */; }; AF20F76A1AF18F9000751A6E /* ABISysV_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF20F7681AF18F9000751A6E /* ABISysV_arm64.cpp */; };
AF20F7701AF1902900751A6E /* RegisterContextLinux_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF20F76E1AF1902900751A6E /* RegisterContextLinux_arm64.cpp */; }; AF20F7701AF1902900751A6E /* RegisterContextLinux_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF20F76E1AF1902900751A6E /* RegisterContextLinux_arm64.cpp */; };
AF23B4DB19009C66003E2A58 /* FreeBSDSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF23B4D919009C66003E2A58 /* FreeBSDSignals.cpp */; }; AF23B4DB19009C66003E2A58 /* FreeBSDSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF23B4D919009C66003E2A58 /* FreeBSDSignals.cpp */; };
AF248A4D1DA71C77000B814D /* TestArm64InstEmulation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF248A4C1DA71C77000B814D /* TestArm64InstEmulation.cpp */; };
AF254E31170CCC33007AE5C9 /* PlatformDarwinKernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF254E2F170CCC33007AE5C9 /* PlatformDarwinKernel.cpp */; }; AF254E31170CCC33007AE5C9 /* PlatformDarwinKernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF254E2F170CCC33007AE5C9 /* PlatformDarwinKernel.cpp */; };
AF25AB26188F685C0030DEC3 /* AppleGetQueuesHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF25AB24188F685C0030DEC3 /* AppleGetQueuesHandler.cpp */; }; AF25AB26188F685C0030DEC3 /* AppleGetQueuesHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF25AB24188F685C0030DEC3 /* AppleGetQueuesHandler.cpp */; };
AF26703A1852D01E00B6CC36 /* Queue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF2670381852D01E00B6CC36 /* Queue.cpp */; }; AF26703A1852D01E00B6CC36 /* Queue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF2670381852D01E00B6CC36 /* Queue.cpp */; };
@ -2922,6 +2923,7 @@
AF20F76F1AF1902900751A6E /* RegisterContextLinux_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextLinux_arm64.h; path = Utility/RegisterContextLinux_arm64.h; sourceTree = "<group>"; }; AF20F76F1AF1902900751A6E /* RegisterContextLinux_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextLinux_arm64.h; path = Utility/RegisterContextLinux_arm64.h; sourceTree = "<group>"; };
AF23B4D919009C66003E2A58 /* FreeBSDSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FreeBSDSignals.cpp; path = Utility/FreeBSDSignals.cpp; sourceTree = "<group>"; }; AF23B4D919009C66003E2A58 /* FreeBSDSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FreeBSDSignals.cpp; path = Utility/FreeBSDSignals.cpp; sourceTree = "<group>"; };
AF23B4DA19009C66003E2A58 /* FreeBSDSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FreeBSDSignals.h; path = Utility/FreeBSDSignals.h; sourceTree = "<group>"; }; AF23B4DA19009C66003E2A58 /* FreeBSDSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FreeBSDSignals.h; path = Utility/FreeBSDSignals.h; sourceTree = "<group>"; };
AF248A4C1DA71C77000B814D /* TestArm64InstEmulation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TestArm64InstEmulation.cpp; path = UnwindAssembly/InstEmulation/TestArm64InstEmulation.cpp; sourceTree = "<group>"; };
AF254E2F170CCC33007AE5C9 /* PlatformDarwinKernel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformDarwinKernel.cpp; sourceTree = "<group>"; }; AF254E2F170CCC33007AE5C9 /* PlatformDarwinKernel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformDarwinKernel.cpp; sourceTree = "<group>"; };
AF254E30170CCC33007AE5C9 /* PlatformDarwinKernel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformDarwinKernel.h; sourceTree = "<group>"; }; AF254E30170CCC33007AE5C9 /* PlatformDarwinKernel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformDarwinKernel.h; sourceTree = "<group>"; };
AF25AB24188F685C0030DEC3 /* AppleGetQueuesHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleGetQueuesHandler.cpp; sourceTree = "<group>"; }; AF25AB24188F685C0030DEC3 /* AppleGetQueuesHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleGetQueuesHandler.cpp; sourceTree = "<group>"; };
@ -6252,6 +6254,14 @@
name = "SysV-arm64"; name = "SysV-arm64";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
AF248A4B1DA71C67000B814D /* InstEmulation */ = {
isa = PBXGroup;
children = (
AF248A4C1DA71C77000B814D /* TestArm64InstEmulation.cpp */,
);
name = InstEmulation;
sourceTree = "<group>";
};
AF2BCA6518C7EFDE005B4526 /* JITLoader */ = { AF2BCA6518C7EFDE005B4526 /* JITLoader */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -6301,7 +6311,7 @@
AFEC5FD31D94F9130076A480 /* UnwindAssembly */ = { AFEC5FD31D94F9130076A480 /* UnwindAssembly */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
AFEC5FD51D94F9380076A480 /* Testx86AssemblyInspectionEngine.cpp */, AF248A4B1DA71C67000B814D /* InstEmulation */,
AFEC5FD41D94F9270076A480 /* x86 */, AFEC5FD41D94F9270076A480 /* x86 */,
); );
name = UnwindAssembly; name = UnwindAssembly;
@ -6310,6 +6320,7 @@
AFEC5FD41D94F9270076A480 /* x86 */ = { AFEC5FD41D94F9270076A480 /* x86 */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
AFEC5FD51D94F9380076A480 /* Testx86AssemblyInspectionEngine.cpp */,
); );
name = x86; name = x86;
sourceTree = "<group>"; sourceTree = "<group>";
@ -6911,6 +6922,7 @@
23CB15351D66DA9300EDDDE1 /* UriParserTest.cpp in Sources */, 23CB15351D66DA9300EDDDE1 /* UriParserTest.cpp in Sources */,
23CB15361D66DA9300EDDDE1 /* FileSpecTest.cpp in Sources */, 23CB15361D66DA9300EDDDE1 /* FileSpecTest.cpp in Sources */,
23E2E5251D90373D006F38BB /* ArchSpecTest.cpp in Sources */, 23E2E5251D90373D006F38BB /* ArchSpecTest.cpp in Sources */,
AF248A4D1DA71C77000B814D /* TestArm64InstEmulation.cpp in Sources */,
23CB15371D66DA9300EDDDE1 /* PythonTestSuite.cpp in Sources */, 23CB15371D66DA9300EDDDE1 /* PythonTestSuite.cpp in Sources */,
23E2E5291D9037D9006F38BB /* SymbolFilePDBTests.cpp in Sources */, 23E2E5291D9037D9006F38BB /* SymbolFilePDBTests.cpp in Sources */,
23E2E5321D903832006F38BB /* BreakpointIDTest.cpp in Sources */, 23E2E5321D903832006F38BB /* BreakpointIDTest.cpp in Sources */,

View File

@ -497,7 +497,7 @@ uint32_t EmulateInstructionARM64::GetFramePointerRegisterNumber() const {
if (m_arch.GetTriple().isAndroid()) if (m_arch.GetTriple().isAndroid())
return LLDB_INVALID_REGNUM; // Don't use frame pointer on android return LLDB_INVALID_REGNUM; // Don't use frame pointer on android
return arm64_dwarf::sp; return arm64_dwarf::fp;
} }
bool EmulateInstructionARM64::UsingAArch32() { bool EmulateInstructionARM64::UsingAArch32() {
@ -693,8 +693,13 @@ bool EmulateInstructionARM64::EmulateADDSUBImm(const uint32_t opcode) {
if (arm64_dwarf::GetRegisterInfo(n, reg_info_Rn)) if (arm64_dwarf::GetRegisterInfo(n, reg_info_Rn))
context.SetRegisterPlusOffset(reg_info_Rn, imm); context.SetRegisterPlusOffset(reg_info_Rn, imm);
if ((n == arm64_dwarf::sp || n == GetFramePointerRegisterNumber()) && if (n == GetFramePointerRegisterNumber() && d == arm64_dwarf::sp &&
d == arm64_dwarf::sp && !setflags) { !setflags) {
// 'mov sp, fp' - common epilogue instruction, CFA is now in terms
// of the stack pointer, instead of frame pointer.
context.type = EmulateInstruction::eContextRestoreStackPointer;
} else if ((n == arm64_dwarf::sp || n == GetFramePointerRegisterNumber()) &&
d == arm64_dwarf::sp && !setflags) {
context.type = EmulateInstruction::eContextAdjustStackPointer; context.type = EmulateInstruction::eContextAdjustStackPointer;
} else if (d == GetFramePointerRegisterNumber() && n == arm64_dwarf::sp && } else if (d == GetFramePointerRegisterNumber() && n == arm64_dwarf::sp &&
!setflags) { !setflags) {

View File

@ -34,6 +34,27 @@ using namespace lldb_private;
bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
AddressRange &range, Thread &thread, UnwindPlan &unwind_plan) { AddressRange &range, Thread &thread, UnwindPlan &unwind_plan) {
std::vector<uint8_t> function_text(range.GetByteSize());
ProcessSP process_sp(thread.GetProcess());
if (process_sp) {
Error error;
const bool prefer_file_cache = true;
if (process_sp->GetTarget().ReadMemory(
range.GetBaseAddress(), prefer_file_cache, function_text.data(),
range.GetByteSize(), error) != range.GetByteSize()) {
return false;
}
}
return GetNonCallSiteUnwindPlanFromAssembly(
range, function_text.data(), function_text.size(), unwind_plan);
}
bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
AddressRange &range, uint8_t *opcode_data, size_t opcode_size,
UnwindPlan &unwind_plan) {
if (opcode_data == nullptr || opcode_size == 0)
return false;
if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() && if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() &&
m_inst_emulator_ap.get()) { m_inst_emulator_ap.get()) {
@ -46,18 +67,16 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
if (unwind_plan.GetRowCount() == 0) if (unwind_plan.GetRowCount() == 0)
return false; return false;
ExecutionContext exe_ctx;
thread.CalculateExecutionContext(exe_ctx);
const bool prefer_file_cache = true; const bool prefer_file_cache = true;
DisassemblerSP disasm_sp(Disassembler::DisassembleRange( DisassemblerSP disasm_sp(Disassembler::DisassembleBytes(
m_arch, NULL, NULL, exe_ctx, range, prefer_file_cache)); m_arch, NULL, NULL, range.GetBaseAddress(), opcode_data, opcode_size,
99999, prefer_file_cache));
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
if (disasm_sp) { if (disasm_sp) {
m_range_ptr = &range; m_range_ptr = &range;
m_thread_ptr = &thread;
m_unwind_plan_ptr = &unwind_plan; m_unwind_plan_ptr = &unwind_plan;
const uint32_t addr_byte_size = m_arch.GetAddressByteSize(); const uint32_t addr_byte_size = m_arch.GetAddressByteSize();
@ -154,8 +173,8 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
m_register_values = it->second.second; m_register_values = it->second.second;
} }
m_inst_emulator_ap->SetInstruction( m_inst_emulator_ap->SetInstruction(inst->GetOpcode(),
inst->GetOpcode(), inst->GetAddress(), exe_ctx.GetTargetPtr()); inst->GetAddress(), nullptr);
if (last_condition != if (last_condition !=
m_inst_emulator_ap->GetInstructionCondition()) { m_inst_emulator_ap->GetInstructionCondition()) {
@ -253,11 +272,10 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
if (log && log->GetVerbose()) { if (log && log->GetVerbose()) {
StreamString strm; StreamString strm;
lldb::addr_t base_addr = lldb::addr_t base_addr = range.GetBaseAddress().GetFileAddress();
range.GetBaseAddress().GetLoadAddress(thread.CalculateTarget().get());
strm.Printf("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):", strm.Printf("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):",
base_addr, base_addr + range.GetByteSize()); base_addr, base_addr + range.GetByteSize());
unwind_plan.Dump(strm, &thread, base_addr); unwind_plan.Dump(strm, nullptr, base_addr);
log->PutCString(strm.GetData()); log->PutCString(strm.GetData());
} }
return unwind_plan.GetRowCount() > 0; return unwind_plan.GetRowCount() > 0;
@ -620,6 +638,19 @@ bool UnwindAssemblyInstEmulation::WriteRegister(
} }
break; break;
case EmulateInstruction::eContextRestoreStackPointer:
if (m_fp_is_cfa) {
m_fp_is_cfa = false;
m_cfa_reg_info = *reg_info;
const uint32_t cfa_reg_num =
reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
assert(cfa_reg_num != LLDB_INVALID_REGNUM);
m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64());
m_curr_row_modified = true;
}
break;
case EmulateInstruction::eContextAdjustStackPointer: case EmulateInstruction::eContextAdjustStackPointer:
// If we have created a frame using the frame pointer, don't follow // If we have created a frame using the frame pointer, don't follow
// subsequent adjustments to the stack pointer. // subsequent adjustments to the stack pointer.

View File

@ -28,6 +28,11 @@ public:
lldb_private::AddressRange &func, lldb_private::Thread &thread, lldb_private::AddressRange &func, lldb_private::Thread &thread,
lldb_private::UnwindPlan &unwind_plan) override; lldb_private::UnwindPlan &unwind_plan) override;
bool
GetNonCallSiteUnwindPlanFromAssembly(lldb_private::AddressRange &func,
uint8_t *opcode_data, size_t opcode_size,
lldb_private::UnwindPlan &unwind_plan);
bool bool
AugmentUnwindPlanFromCallSite(lldb_private::AddressRange &func, AugmentUnwindPlanFromCallSite(lldb_private::AddressRange &func,
lldb_private::Thread &thread, lldb_private::Thread &thread,
@ -67,8 +72,8 @@ private:
UnwindAssemblyInstEmulation(const lldb_private::ArchSpec &arch, UnwindAssemblyInstEmulation(const lldb_private::ArchSpec &arch,
lldb_private::EmulateInstruction *inst_emulator) lldb_private::EmulateInstruction *inst_emulator)
: UnwindAssembly(arch), m_inst_emulator_ap(inst_emulator), : UnwindAssembly(arch), m_inst_emulator_ap(inst_emulator),
m_range_ptr(NULL), m_thread_ptr(NULL), m_unwind_plan_ptr(NULL), m_range_ptr(NULL), m_unwind_plan_ptr(NULL), m_curr_row(),
m_curr_row(), m_cfa_reg_info(), m_fp_is_cfa(false), m_register_values(), m_cfa_reg_info(), m_fp_is_cfa(false), m_register_values(),
m_pushed_regs(), m_curr_row_modified(false), m_pushed_regs(), m_curr_row_modified(false),
m_forward_branch_offset(0) { m_forward_branch_offset(0) {
if (m_inst_emulator_ap.get()) { if (m_inst_emulator_ap.get()) {
@ -130,7 +135,6 @@ private:
std::unique_ptr<lldb_private::EmulateInstruction> m_inst_emulator_ap; std::unique_ptr<lldb_private::EmulateInstruction> m_inst_emulator_ap;
lldb_private::AddressRange *m_range_ptr; lldb_private::AddressRange *m_range_ptr;
lldb_private::Thread *m_thread_ptr;
lldb_private::UnwindPlan *m_unwind_plan_ptr; lldb_private::UnwindPlan *m_unwind_plan_ptr;
lldb_private::UnwindPlan::RowSP m_curr_row; lldb_private::UnwindPlan::RowSP m_curr_row;
typedef std::map<uint64_t, uint64_t> PushedRegisterToAddrMap; typedef std::map<uint64_t, uint64_t> PushedRegisterToAddrMap;

View File

@ -1 +1,2 @@
add_subdirectory(x86) add_subdirectory(x86)
add_subdirectory(InstEmulation)

View File

@ -0,0 +1 @@
add_lldb_unittest(InstEmulationTests TestArm64InstEmulation.cpp)

View File

@ -0,0 +1,166 @@
//===-- TestArm64InstEmulation.cpp ------------------------------------*- C++
//-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "gtest/gtest.h"
#include <vector>
#include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h"
#include "Utility/ARM64_DWARF_Registers.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/AddressRange.h"
#include "lldb/Core/ArchSpec.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Target/UnwindAssembly.h"
#include "Plugins/Disassembler/llvm/DisassemblerLLVMC.h"
#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h"
#include "llvm/Support/TargetSelect.h"
using namespace lldb;
using namespace lldb_private;
class TestArm64InstEmulation : public testing::Test {
public:
// static void SetUpTestCase() { }
// static void TearDownTestCase() { }
// virtual void SetUp() override { }
// virtual void TearDown() override { }
protected:
};
static void init() {
llvm::InitializeAllTargets();
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllDisassemblers();
DisassemblerLLVMC::Initialize();
EmulateInstructionARM64::Initialize();
}
static void terminate() {
DisassemblerLLVMC::Terminate();
EmulateInstructionARM64::Terminate();
}
TEST_F(TestArm64InstEmulation, TestSimpleFunction) {
init();
ArchSpec arch("arm64-apple-ios10", nullptr);
UnwindAssemblyInstEmulation *engine =
static_cast<UnwindAssemblyInstEmulation *>(
UnwindAssemblyInstEmulation::CreateInstance(arch));
EXPECT_TRUE(engine != nullptr);
if (engine == nullptr)
return;
UnwindPlan::RowSP row_sp;
AddressRange sample_range;
UnwindPlan unwind_plan(eRegisterKindLLDB);
UnwindPlan::Row::RegisterLocation regloc;
// 'int main() { }' compiled for arm64-apple-macosx with clang
uint8_t data[] = {
0xfd, 0x7b, 0xbf, 0xa9, // 0xa9bf7bfd : stp x29, x30, [sp, #-0x10]!
0xfd, 0x03, 0x00, 0x91, // 0x910003fd : mov x29, sp
0xff, 0x43, 0x00, 0xd1, // 0xd10043ff : sub sp, sp, #0x10
0xbf, 0x03, 0x00, 0x91, // 0x910003bf : mov sp, x29
0xfd, 0x7b, 0xc1, 0xa8, // 0xa8c17bfd : ldp x29, x30, [sp], #16
0xc0, 0x03, 0x5f, 0xd6, // 0xd65f03c0 : ret
};
// UnwindPlan we expect:
// row[0]: 0: CFA=sp +0 =>
// row[1]: 4: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
// row[2]: 8: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]
// row[2]: 16: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
// row[3]: 20: CFA=sp +0 => fp= <same> lr= <same>
// But this is missing the setup of the frame pointer register (x29) and
// restore of the same. This is a bug in the instruction profiler --
// it won't work if we have a stack frame with a variable length array, or
// an alloca style call where the stack frame size is not fixed. We need
// to recognize the setup of the frame pointer register.
sample_range = AddressRange(0x1000, sizeof(data));
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
sample_range, data, sizeof(data), unwind_plan));
// CFA=sp +0
row_sp = unwind_plan.GetRowForFunctionOffset(0);
EXPECT_EQ(0, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == arm64_dwarf::sp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
// CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(4);
EXPECT_EQ(4, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == arm64_dwarf::sp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(arm64_dwarf::fp, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-16, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(arm64_dwarf::lr, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-8, regloc.GetOffset());
// CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(8);
EXPECT_EQ(8, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == arm64_dwarf::fp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(arm64_dwarf::fp, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-16, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(arm64_dwarf::lr, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-8, regloc.GetOffset());
// CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(16);
EXPECT_EQ(16, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == arm64_dwarf::sp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(arm64_dwarf::fp, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-16, regloc.GetOffset());
EXPECT_TRUE(row_sp->GetRegisterInfo(arm64_dwarf::lr, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_EQ(-8, regloc.GetOffset());
// CFA=sp +0 => fp= <same> lr= <same>
row_sp = unwind_plan.GetRowForFunctionOffset(20);
EXPECT_EQ(20, row_sp->GetOffset());
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == arm64_dwarf::sp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
terminate();
}