llvm-project/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

258 lines
8.5 KiB
C++
Raw Normal View History

//===-- TestDWARFCallFrameInfo.cpp ----------------------------------------===//
//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "gtest/gtest.h"
#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
#include "Plugins/Process/Utility/RegisterContext_x86.h"
#include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h"
[lldb] Add a SubsystemRAII that takes care of calling Initialize and Terminate in the unit tests Summary: Many of our tests need to initialize certain subsystems/plugins of LLDB such as `FileSystem` or `HostInfo` by calling their static `Initialize` functions before the test starts and then calling `::Terminate` after the test is done (in reverse order). This adds a lot of error-prone boilerplate code to our testing code. This patch adds a RAII called SubsystemRAII that ensures that we always call ::Initialize and then call ::Terminate after the test is done (and that the Terminate calls are always in the reverse order of the ::Initialize calls). It also gets rid of all of the boilerplate that we had for these calls. Per-fixture initialization is still not very nice with this approach as it would require some kind of static unique_ptr that gets manually assigned/reseted from the gtest SetUpTestCase/TearDownTestCase functions. Because of that I changed all per-fixture setup to now do per-test setup which can be done by just having the SubsystemRAII as a member of the test fixture. This change doesn't influence our normal test runtime as LIT anyway runs each test case separately (and the Initialize/Terminate calls are anyway not very expensive). It will however make running all tests in a single executable slightly slower. Reviewers: labath, JDevlieghere, martong, espindola, shafik Reviewed By: labath Subscribers: mgorny, rnkovacs, emaste, MaskRay, abidh, lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D71630
2019-12-23 17:38:12 +08:00
#include "TestingSupport/SubsystemRAII.h"
#include "TestingSupport/TestUtilities.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/Section.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Symbol/DWARFCallFrameInfo.h"
#include "lldb/Utility/StreamString.h"
Include inlined functions when figuring out a contiguous address range Checking this in for Antonio Afonso: This diff changes the function LineEntry::GetSameLineContiguousAddressRange so that it also includes function calls that were inlined at the same line of code. My motivation is to decrease the step over time of lines that heavly rely on inlined functions. I have multiple examples in the code base I work that makes a step over stop 20 or mote times internally. This can easly had up to step overs that take >500ms which I was able to lower to 25ms with this new strategy. The reason the current code is not extending the address range beyond an inlined function is because when we resolve the symbol at the next address of the line entry we will get the entry line corresponding to where the original code for the inline function lives, making us barely extend the range. This then will end up on a step over having to stop multiple times everytime there's an inlined function. To check if the range is an inlined function at that line I also get the block associated with the next address and check if there is a parent block with a call site at the line we're trying to extend. To check this I created a new function in Block called GetContainingInlinedBlockWithCallSite that does exactly that. I also added a new function to Declaration for convinence of checking file/line named CompareFileAndLine. To avoid potential issues when extending an address range I added an Extend function that extends the range by the AddressRange given as an argument. This function returns true to indicate sucess when the rage was agumented, false otherwise (e.g.: the ranges are not connected). The reason I do is to make sure that we're not just blindly extending complete_line_range by whatever GetByteSize() we got. If for some reason the ranges are not connected or overlap, or even 0, this could be an issue. I also added a unit tests for this change and include the instructions on the test itself on how to generate the yaml file I use for testing. Differential Revision: https://reviews.llvm.org/D61292 llvm-svn: 360071
2019-05-07 04:01:21 +08:00
#include "llvm/Testing/Support/Error.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
using namespace lldb_private;
using namespace lldb;
class DWARFCallFrameInfoTest : public testing::Test {
[lldb] Add a SubsystemRAII that takes care of calling Initialize and Terminate in the unit tests Summary: Many of our tests need to initialize certain subsystems/plugins of LLDB such as `FileSystem` or `HostInfo` by calling their static `Initialize` functions before the test starts and then calling `::Terminate` after the test is done (in reverse order). This adds a lot of error-prone boilerplate code to our testing code. This patch adds a RAII called SubsystemRAII that ensures that we always call ::Initialize and then call ::Terminate after the test is done (and that the Terminate calls are always in the reverse order of the ::Initialize calls). It also gets rid of all of the boilerplate that we had for these calls. Per-fixture initialization is still not very nice with this approach as it would require some kind of static unique_ptr that gets manually assigned/reseted from the gtest SetUpTestCase/TearDownTestCase functions. Because of that I changed all per-fixture setup to now do per-test setup which can be done by just having the SubsystemRAII as a member of the test fixture. This change doesn't influence our normal test runtime as LIT anyway runs each test case separately (and the Initialize/Terminate calls are anyway not very expensive). It will however make running all tests in a single executable slightly slower. Reviewers: labath, JDevlieghere, martong, espindola, shafik Reviewed By: labath Subscribers: mgorny, rnkovacs, emaste, MaskRay, abidh, lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D71630
2019-12-23 17:38:12 +08:00
SubsystemRAII<FileSystem, HostInfo, ObjectFileELF, SymbolFileSymtab>
subsystems;
protected:
void TestBasic(DWARFCallFrameInfo::Type type, llvm::StringRef symbol);
};
namespace lldb_private {
static std::ostream &operator<<(std::ostream &OS, const UnwindPlan::Row &row) {
StreamString SS;
row.Dump(SS, nullptr, nullptr, 0);
return OS << SS.GetData();
}
} // namespace lldb_private
static UnwindPlan::Row GetExpectedRow0() {
UnwindPlan::Row row;
row.SetOffset(0);
row.GetCFAValue().SetIsRegisterPlusOffset(dwarf_rsp_x86_64, 8);
row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rip_x86_64, -8, false);
return row;
}
static UnwindPlan::Row GetExpectedRow1() {
UnwindPlan::Row row;
row.SetOffset(1);
row.GetCFAValue().SetIsRegisterPlusOffset(dwarf_rsp_x86_64, 16);
row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rip_x86_64, -8, false);
row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rbp_x86_64, -16, false);
return row;
}
static UnwindPlan::Row GetExpectedRow2() {
UnwindPlan::Row row;
row.SetOffset(4);
row.GetCFAValue().SetIsRegisterPlusOffset(dwarf_rbp_x86_64, 16);
row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rip_x86_64, -8, false);
row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rbp_x86_64, -16, false);
return row;
}
void DWARFCallFrameInfoTest::TestBasic(DWARFCallFrameInfo::Type type,
llvm::StringRef symbol) {
auto ExpectedFile = TestFile::fromYaml(R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_DYN
Machine: EM_X86_64
Entry: 0x0000000000000260
Sections:
- Name: .text
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x0000000000000260
AddressAlign: 0x0000000000000010
Content: 554889E5897DFC8B45FC5DC30F1F4000554889E5897DFC8B45FC5DC30F1F4000554889E5897DFC8B45FC5DC3
#0000000000000260 <eh_frame>:
# 260: 55 push %rbp
# 261: 48 89 e5 mov %rsp,%rbp
# 264: 89 7d fc mov %edi,-0x4(%rbp)
# 267: 8b 45 fc mov -0x4(%rbp),%eax
# 26a: 5d pop %rbp
# 26b: c3 retq
# 26c: 0f 1f 40 00 nopl 0x0(%rax)
#
#0000000000000270 <debug_frame3>:
# 270: 55 push %rbp
# 271: 48 89 e5 mov %rsp,%rbp
# 274: 89 7d fc mov %edi,-0x4(%rbp)
# 277: 8b 45 fc mov -0x4(%rbp),%eax
# 27a: 5d pop %rbp
# 27b: c3 retq
# 27c: 0f 1f 40 00 nopl 0x0(%rax)
#
#0000000000000280 <debug_frame4>:
# 280: 55 push %rbp
# 281: 48 89 e5 mov %rsp,%rbp
# 284: 89 7d fc mov %edi,-0x4(%rbp)
# 287: 8b 45 fc mov -0x4(%rbp),%eax
# 28a: 5d pop %rbp
# 28b: c3 retq
- Name: .eh_frame
Type: SHT_X86_64_UNWIND
Flags: [ SHF_ALLOC ]
Address: 0x0000000000000290
AddressAlign: 0x0000000000000008
Content: 1400000000000000017A5200017810011B0C0708900100001C0000001C000000B0FFFFFF0C00000000410E108602430D0600000000000000
#00000000 0000000000000014 00000000 CIE
# Version: 1
# Augmentation: "zR"
# Code alignment factor: 1
# Data alignment factor: -8
# Return address column: 16
# Augmentation data: 1b
#
# DW_CFA_def_cfa: r7 (rsp) ofs 8
# DW_CFA_offset: r16 (rip) at cfa-8
# DW_CFA_nop
# DW_CFA_nop
#
#00000018 000000000000001c 0000001c FDE cie=00000000 pc=ffffffffffffffd0..ffffffffffffffdc
# DW_CFA_advance_loc: 1 to ffffffffffffffd1
# DW_CFA_def_cfa_offset: 16
# DW_CFA_offset: r6 (rbp) at cfa-16
# DW_CFA_advance_loc: 3 to ffffffffffffffd4
# DW_CFA_def_cfa_register: r6 (rbp)
# DW_CFA_nop
# DW_CFA_nop
# DW_CFA_nop
# DW_CFA_nop
# DW_CFA_nop
# DW_CFA_nop
# DW_CFA_nop
- Name: .debug_frame
Type: SHT_PROGBITS
AddressAlign: 0x0000000000000008
Content: 14000000FFFFFFFF03000178100C070890010000000000001C0000000000000070020000000000000C00000000000000410E108602430D0614000000FFFFFFFF040008000178100C07089001000000001C0000003800000080020000000000000C00000000000000410E108602430D06
#00000000 0000000000000014 ffffffff CIE
# Version: 3
# Augmentation: ""
# Code alignment factor: 1
# Data alignment factor: -8
# Return address column: 16
#
# DW_CFA_def_cfa: r7 (rsp) ofs 8
# DW_CFA_offset: r16 (rip) at cfa-8
# DW_CFA_nop
# DW_CFA_nop
# DW_CFA_nop
# DW_CFA_nop
# DW_CFA_nop
# DW_CFA_nop
#
#00000018 000000000000001c 00000000 FDE cie=00000000 pc=0000000000000270..000000000000027c
# DW_CFA_advance_loc: 1 to 0000000000000271
# DW_CFA_def_cfa_offset: 16
# DW_CFA_offset: r6 (rbp) at cfa-16
# DW_CFA_advance_loc: 3 to 0000000000000274
# DW_CFA_def_cfa_register: r6 (rbp)
#
#00000038 0000000000000014 ffffffff CIE
# Version: 4
# Augmentation: ""
# Pointer Size: 8
# Segment Size: 0
# Code alignment factor: 1
# Data alignment factor: -8
# Return address column: 16
#
# DW_CFA_def_cfa: r7 (rsp) ofs 8
# DW_CFA_offset: r16 (rip) at cfa-8
# DW_CFA_nop
# DW_CFA_nop
# DW_CFA_nop
# DW_CFA_nop
#
#00000050 000000000000001c 00000038 FDE cie=00000038 pc=0000000000000280..000000000000028c
# DW_CFA_advance_loc: 1 to 0000000000000281
# DW_CFA_def_cfa_offset: 16
# DW_CFA_offset: r6 (rbp) at cfa-16
# DW_CFA_advance_loc: 3 to 0000000000000284
# DW_CFA_def_cfa_register: r6 (rbp)
Symbols:
- Name: eh_frame
Type: STT_FUNC
Section: .text
Value: 0x0000000000000260
Size: 0x000000000000000C
Binding: STB_GLOBAL
- Name: debug_frame3
Type: STT_FUNC
Section: .text
Value: 0x0000000000000270
Size: 0x000000000000000C
Binding: STB_GLOBAL
- Name: debug_frame4
Type: STT_FUNC
Section: .text
Value: 0x0000000000000280
Size: 0x000000000000000C
Binding: STB_GLOBAL
...
)");
ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec());
SectionList *list = module_sp->GetSectionList();
ASSERT_NE(nullptr, list);
auto section_sp = list->FindSectionByType(type == DWARFCallFrameInfo::EH
? eSectionTypeEHFrame
: eSectionTypeDWARFDebugFrame,
false);
ASSERT_NE(nullptr, section_sp);
DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp, type);
const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType(
ConstString(symbol), eSymbolTypeAny);
ASSERT_NE(nullptr, sym);
UnwindPlan plan(eRegisterKindGeneric);
ASSERT_TRUE(cfi.GetUnwindPlan(sym->GetAddress(), plan));
ASSERT_EQ(3, plan.GetRowCount());
EXPECT_EQ(GetExpectedRow0(), *plan.GetRowAtIndex(0));
EXPECT_EQ(GetExpectedRow1(), *plan.GetRowAtIndex(1));
EXPECT_EQ(GetExpectedRow2(), *plan.GetRowAtIndex(2));
}
TEST_F(DWARFCallFrameInfoTest, Basic_dwarf3) {
TestBasic(DWARFCallFrameInfo::DWARF, "debug_frame3");
}
TEST_F(DWARFCallFrameInfoTest, Basic_dwarf4) {
TestBasic(DWARFCallFrameInfo::DWARF, "debug_frame4");
}
TEST_F(DWARFCallFrameInfoTest, Basic_eh) {
TestBasic(DWARFCallFrameInfo::EH, "eh_frame");
}