Add a unit test for an x86_64 assembly inspection of

a large stack frame with lots of spilled registers.

While writing the i386 version of this test, it looks
like I found a bug in the 32-bit instruction profiler
code.  I may ned to fix the assembly inspection engine
before I can finish writing that test, so I'm only
committing the 64-bit one tonight.

<rdar://problem/28509178> 

llvm-svn: 282683
This commit is contained in:
Jason Molenda
2016-09-29 04:01:43 +00:00
parent 29f52fb02c
commit 415f732249

View File

@@ -43,27 +43,28 @@ protected:
// names should match the constants below. These will be the eRegisterKindLLDB
// register numbers.
const char *x86_64_reg_names[] = {"rax", "rcx", "rdx", "rsp", "rbp", "rsi",
"rdi", "r8", "r9", "r10", "r11", "r12",
"r13", "r14", "r15", "rip"};
const char *x86_64_reg_names[] = {"rax", "rbx", "rcx", "rdx", "rsp", "rbp",
"rsi", "rdi", "r8", "r9", "r10", "r11",
"r12", "r13", "r14", "r15", "rip"};
enum x86_64_regs {
k_rax = 0,
k_rcx = 1,
k_rdx = 2,
k_rsp = 3,
k_rbp = 4,
k_rsi = 5,
k_rdi = 6,
k_r8 = 7,
k_r9 = 8,
k_r10 = 9,
k_r11 = 10,
k_r12 = 11,
k_r13 = 12,
k_r14 = 13,
k_r15 = 14,
k_rip = 15
k_rbx = 1,
k_rcx = 2,
k_rdx = 3,
k_rsp = 4,
k_rbp = 5,
k_rsi = 6,
k_rdi = 7,
k_r8 = 8,
k_r9 = 9,
k_r10 = 10,
k_r11 = 11,
k_r12 = 12,
k_r13 = 13,
k_r14 = 14,
k_r15 = 15,
k_rip = 16
};
// names should match the constants below. These will be the eRegisterKindLLDB
@@ -133,6 +134,7 @@ std::unique_ptr<x86AssemblyInspectionEngine> Geti386Inspector() {
TEST_F(Testx86AssemblyInspectionEngine, TestSimple64bitFrameFunction) {
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
// 'int main() { }' compiled for x86_64-apple-macosx with clang
uint8_t data[] = {
0x55, // offset 0 -- pushq %rbp
0x48, 0x89, 0xe5, // offset 1 -- movq %rsp, %rbp
@@ -208,6 +210,7 @@ TEST_F(Testx86AssemblyInspectionEngine, TestSimple64bitFrameFunction) {
TEST_F(Testx86AssemblyInspectionEngine, TestSimple32bitFrameFunction) {
std::unique_ptr<x86AssemblyInspectionEngine> engine = Geti386Inspector();
// 'int main() { }' compiled for i386-apple-macosx with clang
uint8_t data[] = {
0x55, // offset 0 -- pushl %ebp
0x89, 0xe5, // offset 1 -- movl %esp, %ebp
@@ -279,3 +282,159 @@ TEST_F(Testx86AssemblyInspectionEngine, TestSimple32bitFrameFunction) {
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -4);
}
TEST_F(Testx86AssemblyInspectionEngine, Test64bitFramelessBigStackFrame) {
std::unique_ptr<x86AssemblyInspectionEngine> engine = Getx86_64Inspector();
// this source file:
//
// #include <stdio.h>
// int main (int argc, char **argv)
// {
//
// const int arrsize = 60;
// int buf[arrsize * arrsize];
// int accum = argc;
// for (int i = 0; i < arrsize; i++)
// for (int j = 0; j < arrsize; j++)
// {
// if (i > 0 && j > 0)
// {
// int n = buf[(i-1) * (j-1)] * 2;
// int m = buf[(i-1) * (j-1)] / 2;
// int j = buf[(i-1) * (j-1)] + 2;
// int k = buf[(i-1) * (j-1)] - 2;
// printf ("%d ", n + m + j + k);
// buf[(i-1) * (j-1)] += n - m + j - k;
// }
// buf[i*j] = accum++;
// }
//
// return buf[(arrsize * arrsize) - 2] + printf ("%d\n", buf[(arrsize *
// arrsize) - 3]);
// }
//
// compiled 'clang -fomit-frame-pointer -Os' for x86_64-apple-macosx
uint8_t data[] = {
0x55, // offset 0 -- pushq %rbp
0x41, 0x57, // offset 1 -- pushq %r15
0x41, 0x56, // offset 3 -- pushq %r14
0x41, 0x55, // offset 5 -- pushq %r13
0x41, 0x54, // offset 7 -- pushq %r12
0x53, // offset 9 -- pushq %rbx
0x48, 0x81, 0xec, 0x68, 0x38, 0x00,
0x00, // offset 10 -- subq $0x3868, %rsp
// ....
0x48, 0x81, 0xc4, 0x68, 0x38, 0x00,
0x00, // offset 17 -- addq $0x3868, %rsp
0x5b, // offset 24 -- popq %rbx
0x41, 0x5c, // offset 25 -- popq %r12
0x41, 0x5d, // offset 27 -- popq %r13
0x41, 0x5e, // offset 29 -- popq %r14
0x41, 0x5f, // offset 31 -- popq %r15
0x5d, // offset 33 -- popq %rbp
0xc3, // offset 34 -- retq
0xe8, 0x00, 0x00, 0x00, 0x00 // offset 35 -- callq whatever
};
AddressRange sample_range(0x1000, sizeof(data));
UnwindPlan unwind_plan(eRegisterKindLLDB);
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
data, sizeof(data), sample_range, unwind_plan));
// Unwind rules should look like
// 0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
// 1: CFA=rsp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
// 3: CFA=rsp+24 => rbp=[CFA-16] rsp=CFA+0 r15=[CFA-24] rip=[CFA-8]
// 5: CFA=rsp+32 => rbp=[CFA-16] rsp=CFA+0 r14=[CFA-32] r15=[CFA-24]
// rip=[CFA-8
// 7: CFA=rsp+40 => rbp=[CFA-16] rsp=CFA+0 r13=[CFA-40] r14=[CFA-32]
// r15=[CFA-24] rip=[CFA-8]
// 9: CFA=rsp+48 => rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48] r13=[CFA-40]
// r14=[CFA-32] r15=[CFA-24] rip=[CFA-8]
// 10: CFA=rsp+56 => rbx=[CFA-56] rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48]
// r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] rip=[CFA-8]
// 17: CFA=rsp+14496 => rbx=[CFA-56] rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48]
// r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] rip=[CFA-8]
// 24: CFA=rsp+56 => rbx=[CFA-56] rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48]
// r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] rip=[CFA-8]
// 25: CFA=rsp+48 => rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48] r13=[CFA-40]
// r14=[CFA-32] r15=[CFA-24] rip=[CFA-8]
// 27: CFA=rsp+40 => rbp=[CFA-16] rsp=CFA+0 r13=[CFA-40] r14=[CFA-32]
// r15=[CFA-24] rip=[CFA-8]
// 29: CFA=rsp+32 => rbp=[CFA-16] rsp=CFA+0 r14=[CFA-32] r15=[CFA-24]
// rip=[CFA-8]
// 31: CFA=rsp+24 => rbp=[CFA-16] rsp=CFA+0 r15=[CFA-24] rip=[CFA-8]
// 33: CFA=rsp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8]
// 34: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
UnwindPlan::Row::RegisterLocation regloc;
// grab the Row for when the prologue has finished executing:
// 17: CFA=rsp+14496 => rbx=[CFA-56] rbp=[CFA-16] rsp=CFA+0 r12=[CFA-48]
// r13=[CFA-40] r14=[CFA-32] r15=[CFA-24] rip=[CFA-8]
UnwindPlan::RowSP row_sp = unwind_plan.GetRowForFunctionOffset(17);
EXPECT_TRUE(row_sp->GetOffset() == 17);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_TRUE(row_sp->GetCFAValue().GetOffset() == 14496);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -8);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbp, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -16);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r15, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -24);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r14, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -32);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r13, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -40);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r12, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -48);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbx, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -56);
// grab the Row for when the epiloge has finished executing:
// 34: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
row_sp = unwind_plan.GetRowForFunctionOffset(34);
EXPECT_TRUE(row_sp->GetOffset() == 34);
EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == k_rsp);
EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
EXPECT_TRUE(row_sp->GetCFAValue().GetOffset() == 8);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rip, regloc));
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
EXPECT_TRUE(regloc.GetOffset() == -8);
// these could be set to IsSame and be valid -- meaning that the
// register value is the same as the caller's -- but I'd rather
// they not be mentioned at all.
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbp, regloc) == false);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r15, regloc) == false);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r14, regloc) == false);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r13, regloc) == false);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_r12, regloc) == false);
EXPECT_TRUE(row_sp->GetRegisterInfo(k_rbx, regloc) == false);
}