brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.2 KiB · ca63c6f Raw
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