305 lines · cpp
1//===-- LineTableTest.cpp -------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"10#include "TestingSupport/SubsystemRAII.h"11#include "TestingSupport/TestUtilities.h"12#include "lldb/Core/PluginManager.h"13#include "lldb/Symbol/CompileUnit.h"14#include "lldb/Symbol/SymbolFile.h"15#include "gtest/gtest.h"16#include <memory>17 18using namespace lldb;19using namespace llvm;20using namespace lldb_private;21 22namespace {23 24// A fake symbol file class to allow us to create the line table "the right25// way". Pretty much all methods except for GetCompileUnitAtIndex and26// GetNumCompileUnits are stubbed out.27class FakeSymbolFile : public SymbolFile {28public:29 /// LLVM RTTI support.30 /// \{31 bool isA(const void *ClassID) const override {32 return ClassID == &ID || SymbolFile::isA(ClassID);33 }34 static bool classof(const SymbolFile *obj) { return obj->isA(&ID); }35 /// \}36 37 static void Initialize() {38 PluginManager::RegisterPlugin("FakeSymbolFile", "", CreateInstance,39 DebuggerInitialize);40 }41 static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }42 43 void InjectCompileUnit(std::unique_ptr<CompileUnit> cu_up) {44 m_cu_sp = std::move(cu_up);45 }46 47private:48 /// LLVM RTTI support.49 static char ID;50 51 static SymbolFile *CreateInstance(ObjectFileSP objfile_sp) {52 return new FakeSymbolFile(std::move(objfile_sp));53 }54 static void DebuggerInitialize(Debugger &) {}55 56 StringRef GetPluginName() override { return "FakeSymbolFile"; }57 uint32_t GetAbilities() override { return UINT32_MAX; }58 uint32_t CalculateAbilities() override { return UINT32_MAX; }59 uint32_t GetNumCompileUnits() override { return 1; }60 CompUnitSP GetCompileUnitAtIndex(uint32_t) override { return m_cu_sp; }61 Symtab *GetSymtab(bool can_create = true) override { return nullptr; }62 LanguageType ParseLanguage(CompileUnit &) override { return eLanguageTypeC; }63 size_t ParseFunctions(CompileUnit &) override { return 0; }64 bool ParseLineTable(CompileUnit &) override { return true; }65 bool ParseDebugMacros(CompileUnit &) override { return true; }66 bool ParseSupportFiles(CompileUnit &, SupportFileList &) override {67 return true;68 }69 size_t ParseTypes(CompileUnit &) override { return 0; }70 bool ParseImportedModules(const SymbolContext &,71 std::vector<SourceModule> &) override {72 return false;73 }74 size_t ParseBlocksRecursive(Function &) override { return 0; }75 size_t ParseVariablesForContext(const SymbolContext &) override { return 0; }76 Type *ResolveTypeUID(user_id_t) override { return nullptr; }77 std::optional<ArrayInfo>78 GetDynamicArrayInfoForUID(user_id_t, const ExecutionContext *) override {79 return std::nullopt;80 }81 bool CompleteType(CompilerType &) override { return true; }82 uint32_t ResolveSymbolContext(const Address &, SymbolContextItem,83 SymbolContext &) override {84 return 0;85 }86 void GetTypes(SymbolContextScope *, TypeClass, TypeList &) override {}87 Expected<TypeSystemSP> GetTypeSystemForLanguage(LanguageType) override {88 return createStringError(std::errc::not_supported, "");89 }90 const ObjectFile *GetObjectFile() const override {91 return m_objfile_sp.get();92 }93 ObjectFile *GetObjectFile() override { return m_objfile_sp.get(); }94 ObjectFile *GetMainObjectFile() override { return m_objfile_sp.get(); }95 void SectionFileAddressesChanged() override {}96 void Dump(Stream &) override {}97 uint64_t GetDebugInfoSize(bool) override { return 0; }98 bool GetDebugInfoIndexWasLoadedFromCache() const override { return false; }99 void SetDebugInfoIndexWasLoadedFromCache() override {}100 bool GetDebugInfoIndexWasSavedToCache() const override { return false; }101 void SetDebugInfoIndexWasSavedToCache() override {}102 bool GetDebugInfoHadFrameVariableErrors() const override { return false; }103 void SetDebugInfoHadFrameVariableErrors() override {}104 TypeSP MakeType(user_id_t, ConstString, std::optional<uint64_t>,105 SymbolContextScope *, user_id_t, Type::EncodingDataType,106 const Declaration &, const CompilerType &, Type::ResolveState,107 uint32_t) override {108 return nullptr;109 }110 TypeSP CopyType(const TypeSP &) override { return nullptr; }111 112 FakeSymbolFile(ObjectFileSP objfile_sp)113 : m_objfile_sp(std::move(objfile_sp)) {}114 115 ObjectFileSP m_objfile_sp;116 CompUnitSP m_cu_sp;117};118 119struct FakeModuleFixture {120 TestFile file;121 ModuleSP module_sp;122 SectionSP text_sp;123 LineTable *line_table;124};125 126class LineTableTest : public testing::Test {127 SubsystemRAII<ObjectFileELF, FakeSymbolFile> subsystems;128};129 130class LineSequenceBuilder {131public:132 std::vector<LineTable::Sequence> Build() { return std::move(m_sequences); }133 enum Terminal : bool { Terminal = true };134 void Entry(addr_t addr, bool terminal = false) {135 LineTable::AppendLineEntryToSequence(136 m_sequence, addr, /*line=*/1, /*column=*/0,137 /*file_idx=*/0,138 /*is_start_of_statement=*/false, /*is_start_of_basic_block=*/false,139 /*is_prologue_end=*/false, /*is_epilogue_begin=*/false, terminal);140 if (terminal)141 m_sequences.push_back(std::move(m_sequence));142 }143 144private:145 std::vector<LineTable::Sequence> m_sequences;146 LineTable::Sequence m_sequence;147};148 149} // namespace150 151char FakeSymbolFile::ID;152 153static llvm::Expected<FakeModuleFixture>154CreateFakeModule(std::vector<LineTable::Sequence> line_sequences) {155 Expected<TestFile> file = TestFile::fromYaml(R"(156--- !ELF157FileHeader:158 Class: ELFCLASS64159 Data: ELFDATA2LSB160 Type: ET_EXEC161 Machine: EM_386162Sections:163 - Name: .text164 Type: SHT_PROGBITS165 Flags: [ SHF_ALLOC, SHF_EXECINSTR ]166 AddressAlign: 0x0010167 Address: 0x0000168 Size: 0x1000169)");170 if (!file)171 return file.takeError();172 173 auto module_sp = std::make_shared<Module>(file->moduleSpec());174 SectionSP text_sp =175 module_sp->GetSectionList()->FindSectionByName(ConstString(".text"));176 if (!text_sp)177 return createStringError("No .text");178 179 auto cu_up = std::make_unique<CompileUnit>(180 module_sp,181 /*user_data=*/nullptr,182 /*support_file_nsp=*/std::make_shared<SupportFile>(),183 /*uid=*/0, eLanguageTypeC,184 /*is_optimized=*/eLazyBoolNo);185 LineTable *line_table = new LineTable(cu_up.get(), std::move(line_sequences));186 cu_up->SetLineTable(line_table);187 cast<FakeSymbolFile>(module_sp->GetSymbolFile())188 ->InjectCompileUnit(std::move(cu_up));189 190 return FakeModuleFixture{std::move(*file), std::move(module_sp),191 std::move(text_sp), line_table};192}193 194TEST_F(LineTableTest, lower_bound) {195 LineSequenceBuilder builder;196 builder.Entry(0);197 builder.Entry(10);198 builder.Entry(20, LineSequenceBuilder::Terminal);199 builder.Entry(20); // Starts right after the previous sequence.200 builder.Entry(30, LineSequenceBuilder::Terminal);201 builder.Entry(40); // Gap after the previous sequence.202 builder.Entry(50, LineSequenceBuilder::Terminal);203 204 llvm::Expected<FakeModuleFixture> fixture = CreateFakeModule(builder.Build());205 ASSERT_THAT_EXPECTED(fixture, llvm::Succeeded());206 207 LineTable *table = fixture->line_table;208 209 auto make_addr = [&](addr_t addr) { return Address(fixture->text_sp, addr); };210 211 EXPECT_EQ(table->lower_bound(make_addr(0)), 0u);212 EXPECT_EQ(table->lower_bound(make_addr(9)), 0u);213 EXPECT_EQ(table->lower_bound(make_addr(10)), 1u);214 EXPECT_EQ(table->lower_bound(make_addr(19)), 1u);215 216 // Skips over the terminal entry.217 EXPECT_EQ(table->lower_bound(make_addr(20)), 3u);218 EXPECT_EQ(table->lower_bound(make_addr(29)), 3u);219 220 // In case there's no "real" entry at this address, the function returns the221 // first real entry.222 EXPECT_EQ(table->lower_bound(make_addr(30)), 5u);223 EXPECT_EQ(table->lower_bound(make_addr(40)), 5u);224 225 // In a gap, return the first entry after the gap.226 EXPECT_EQ(table->lower_bound(make_addr(39)), 5u);227 228 // And if there's no such entry, return the size of the list.229 EXPECT_EQ(table->lower_bound(make_addr(50)), table->GetSize());230 EXPECT_EQ(table->lower_bound(make_addr(59)), table->GetSize());231}232 233TEST_F(LineTableTest, GetLineEntryIndexRange) {234 LineSequenceBuilder builder;235 builder.Entry(0);236 builder.Entry(10);237 builder.Entry(20, LineSequenceBuilder::Terminal);238 239 llvm::Expected<FakeModuleFixture> fixture = CreateFakeModule(builder.Build());240 ASSERT_THAT_EXPECTED(fixture, llvm::Succeeded());241 242 LineTable *table = fixture->line_table;243 244 auto make_range = [&](addr_t addr, addr_t size) {245 return AddressRange(fixture->text_sp, addr, size);246 };247 248 EXPECT_THAT(table->GetLineEntryIndexRange(make_range(0, 10)),249 testing::Pair(0, 1));250 EXPECT_THAT(table->GetLineEntryIndexRange(make_range(0, 20)),251 testing::Pair(0, 3)); // Includes the terminal entry.252 // Partial overlap on one side.253 EXPECT_THAT(table->GetLineEntryIndexRange(make_range(3, 7)),254 testing::Pair(0, 1));255 // On the other side256 EXPECT_THAT(table->GetLineEntryIndexRange(make_range(0, 15)),257 testing::Pair(0, 2));258 // On both sides259 EXPECT_THAT(table->GetLineEntryIndexRange(make_range(2, 3)),260 testing::Pair(0, 1));261 // Empty ranges262 EXPECT_THAT(table->GetLineEntryIndexRange(make_range(0, 0)),263 testing::Pair(0, 0));264 EXPECT_THAT(table->GetLineEntryIndexRange(make_range(5, 0)),265 testing::Pair(0, 0));266 EXPECT_THAT(table->GetLineEntryIndexRange(make_range(10, 0)),267 testing::Pair(1, 1));268}269 270TEST_F(LineTableTest, FindLineEntryByAddress) {271 LineSequenceBuilder builder;272 builder.Entry(0);273 builder.Entry(10);274 builder.Entry(20, LineSequenceBuilder::Terminal);275 builder.Entry(20); // Starts right after the previous sequence.276 builder.Entry(30, LineSequenceBuilder::Terminal);277 builder.Entry(40); // Gap after the previous sequence.278 builder.Entry(50, LineSequenceBuilder::Terminal);279 280 llvm::Expected<FakeModuleFixture> fixture = CreateFakeModule(builder.Build());281 ASSERT_THAT_EXPECTED(fixture, llvm::Succeeded());282 283 LineTable *table = fixture->line_table;284 285 auto find = [&](addr_t addr) -> std::tuple<addr_t, addr_t, bool> {286 LineEntry entry;287 if (!table->FindLineEntryByAddress(Address(fixture->text_sp, addr), entry))288 return {LLDB_INVALID_ADDRESS, LLDB_INVALID_ADDRESS, false};289 return {entry.range.GetBaseAddress().GetFileAddress(),290 entry.range.GetByteSize(),291 static_cast<bool>(entry.is_terminal_entry)};292 };293 294 EXPECT_THAT(find(0), testing::FieldsAre(0, 10, false));295 EXPECT_THAT(find(9), testing::FieldsAre(0, 10, false));296 EXPECT_THAT(find(10), testing::FieldsAre(10, 10, false));297 EXPECT_THAT(find(19), testing::FieldsAre(10, 10, false));298 EXPECT_THAT(find(20), testing::FieldsAre(20, 10, false));299 EXPECT_THAT(find(30), testing::FieldsAre(LLDB_INVALID_ADDRESS,300 LLDB_INVALID_ADDRESS, false));301 EXPECT_THAT(find(40), testing::FieldsAre(40, 10, false));302 EXPECT_THAT(find(50), testing::FieldsAre(LLDB_INVALID_ADDRESS,303 LLDB_INVALID_ADDRESS, false));304}305