diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h --- a/lldb/include/lldb/Core/Module.h +++ b/lldb/include/lldb/Core/Module.h @@ -958,6 +958,12 @@ ///by \a m_file. uint64_t m_object_offset; llvm::sys::TimePoint<> m_object_mod_time; + + /// DataBuffer containing the module image, if it was provided at + /// construction time. Otherwise the data will be retrieved by mapping + /// one of the FileSpec members above. + lldb::DataBufferSP m_data_sp; + lldb::ObjectFileSP m_objfile_sp; ///< A shared pointer to the object file ///parser for this module as it may or may ///not be shared with the SymbolFile diff --git a/lldb/include/lldb/Core/ModuleSpec.h b/lldb/include/lldb/Core/ModuleSpec.h --- a/lldb/include/lldb/Core/ModuleSpec.h +++ b/lldb/include/lldb/Core/ModuleSpec.h @@ -30,11 +30,15 @@ m_object_name(), m_object_offset(0), m_object_size(0), m_source_mappings() {} - ModuleSpec(const FileSpec &file_spec, const UUID &uuid = UUID()) + /// If the \param data argument is passed, its contents will be used + /// as the module contents instead of trying to read them from + /// \param file_spec. + ModuleSpec(const FileSpec &file_spec, const UUID &uuid = UUID(), + lldb::DataBufferSP data = lldb::DataBufferSP()) : m_file(file_spec), m_platform_file(), m_symbol_file(), m_arch(), m_uuid(uuid), m_object_name(), m_object_offset(0), m_object_size(FileSystem::Instance().GetByteSize(file_spec)), - m_source_mappings() {} + m_source_mappings(), m_data(data) {} ModuleSpec(const FileSpec &file_spec, const ArchSpec &arch) : m_file(file_spec), m_platform_file(), m_symbol_file(), m_arch(arch), @@ -62,6 +66,7 @@ m_object_size = rhs.m_object_size; m_object_mod_time = rhs.m_object_mod_time; m_source_mappings = rhs.m_source_mappings; + m_data = rhs.m_data; } return *this; } @@ -146,6 +151,8 @@ PathMappingList &GetSourceMappingList() const { return m_source_mappings; } + lldb::DataBufferSP GetData() const { return m_data; } + void Clear() { m_file.Clear(); m_platform_file.Clear(); @@ -289,6 +296,7 @@ uint64_t m_object_size; llvm::sys::TimePoint<> m_object_mod_time; mutable PathMappingList m_source_mappings; + lldb::DataBufferSP m_data = {}; }; class ModuleSpecList { diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -172,10 +172,10 @@ lldb::addr_t header_addr, lldb::DataBufferSP &file_data_sp); - static size_t GetModuleSpecifications(const FileSpec &file, - lldb::offset_t file_offset, - lldb::offset_t file_size, - ModuleSpecList &specs); + static size_t + GetModuleSpecifications(const FileSpec &file, lldb::offset_t file_offset, + lldb::offset_t file_size, ModuleSpecList &specs, + lldb::DataBufferSP data_sp = lldb::DataBufferSP()); static size_t GetModuleSpecifications(const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, diff --git a/lldb/include/lldb/Utility/DataBuffer.h b/lldb/include/lldb/Utility/DataBuffer.h --- a/lldb/include/lldb/Utility/DataBuffer.h +++ b/lldb/include/lldb/Utility/DataBuffer.h @@ -79,6 +79,20 @@ } }; +class DataBufferUnowned : public DataBuffer { +public: + DataBufferUnowned(uint8_t *bytes, lldb::offset_t size) + : m_bytes(bytes), m_size(size) {} + + uint8_t *GetBytes() override { return m_bytes; } + const uint8_t *GetBytes() const override { return m_bytes; } + lldb::offset_t GetByteSize() const override { return m_size; } + +private: + uint8_t *m_bytes; + lldb::offset_t m_size; +}; + } // namespace lldb_private #endif /// #if defined(__cplusplus) diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -147,11 +147,13 @@ : module_spec.GetObjectName().AsCString(""), module_spec.GetObjectName().IsEmpty() ? "" : ")"); + auto data_sp = module_spec.GetData(); + // First extract all module specifications from the file using the local file // path. If there are no specifications, then don't fill anything in ModuleSpecList modules_specs; if (ObjectFile::GetModuleSpecifications(module_spec.GetFileSpec(), 0, 0, - modules_specs) == 0) + modules_specs, data_sp) == 0) return; // Now make sure that one of the module specifications matches what we just @@ -170,11 +172,20 @@ return; } - if (module_spec.GetFileSpec()) - m_mod_time = FileSystem::Instance().GetModificationTime(module_spec.GetFileSpec()); - else if (matching_module_spec.GetFileSpec()) - m_mod_time = - FileSystem::Instance().GetModificationTime(matching_module_spec.GetFileSpec()); + // Set m_data_sp if it was initially provided in the ModuleSpec. Note that + // we cannot use the data_sp variable here, because it will have been + // modified by GetModuleSpecifications(). + if (auto module_spec_data_sp = module_spec.GetData()) { + m_data_sp = module_spec_data_sp; + m_mod_time = {}; + } else { + if (module_spec.GetFileSpec()) + m_mod_time = + FileSystem::Instance().GetModificationTime(module_spec.GetFileSpec()); + else if (matching_module_spec.GetFileSpec()) + m_mod_time = FileSystem::Instance().GetModificationTime( + matching_module_spec.GetFileSpec()); + } // Copy the architecture from the actual spec if we got one back, else use // the one that was specified @@ -1110,6 +1121,10 @@ } bool Module::FileHasChanged() const { + // We have provided the DataBuffer for this module to avoid accessing the + // filesystem. We never want to reload those files. + if (m_data_sp) + return false; if (!m_file_has_changed) m_file_has_changed = (FileSystem::Instance().GetModificationTime(m_file) != m_mod_time); @@ -1229,12 +1244,19 @@ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, "Module::GetObjectFile () module = %s", GetFileSpec().GetFilename().AsCString("")); - DataBufferSP data_sp; lldb::offset_t data_offset = 0; - const lldb::offset_t file_size = - FileSystem::Instance().GetByteSize(m_file); + lldb::offset_t file_size; + + if (m_data_sp) + file_size = m_data_sp->GetByteSize(); + else + file_size = FileSystem::Instance().GetByteSize(m_file); + if (file_size > m_object_offset) { m_did_load_objfile = true; + // FindPlugin will modify its data_sp argument. Do not let it + // modify our m_data_sp member. + auto data_sp = m_data_sp; m_objfile_sp = ObjectFile::FindPlugin( shared_from_this(), &m_file, m_object_offset, file_size - m_object_offset, data_sp, data_offset); diff --git a/lldb/source/Symbol/ObjectFile.cpp b/lldb/source/Symbol/ObjectFile.cpp --- a/lldb/source/Symbol/ObjectFile.cpp +++ b/lldb/source/Symbol/ObjectFile.cpp @@ -207,9 +207,11 @@ size_t ObjectFile::GetModuleSpecifications(const FileSpec &file, lldb::offset_t file_offset, lldb::offset_t file_size, - ModuleSpecList &specs) { - DataBufferSP data_sp = - FileSystem::Instance().CreateDataBuffer(file.GetPath(), 512, file_offset); + ModuleSpecList &specs, + DataBufferSP data_sp) { + if (!data_sp) + data_sp = FileSystem::Instance().CreateDataBuffer(file.GetPath(), 512, + file_offset); if (data_sp) { if (file_size == 0) { const lldb::offset_t actual_file_size = diff --git a/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp b/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp --- a/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp +++ b/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp @@ -88,12 +88,13 @@ Size: 0x0000000000000004 Binding: STB_GLOBAL ... -)"); +)", + true); ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); - ModuleSpec spec{FileSpec(ExpectedFile->name())}; - spec.GetSymbolFileSpec().SetFile(ExpectedFile->name(), - FileSpec::Style::native); + ModuleSpec spec{FileSpec(""), UUID(), + ExpectedFile->dataBuffer()}; + // spec.GetSymbolFileSpec() = spec; auto module_sp = std::make_shared(spec); SectionList *list = module_sp->GetSectionList(); ASSERT_NE(nullptr, list); diff --git a/lldb/unittests/Symbol/TestLineEntry.cpp b/lldb/unittests/Symbol/TestLineEntry.cpp --- a/lldb/unittests/Symbol/TestLineEntry.cpp +++ b/lldb/unittests/Symbol/TestLineEntry.cpp @@ -46,10 +46,11 @@ }; void LineEntryTest::SetUp() { - auto ExpectedFile = TestFile::fromYamlFile("inlined-functions.yaml"); + auto ExpectedFile = TestFile::fromYamlFile("inlined-functions.yaml", true); ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); m_file.emplace(std::move(*ExpectedFile)); - m_module_sp = std::make_shared(ModuleSpec(FileSpec(m_file->name()))); + m_module_sp = std::make_shared( + ModuleSpec(FileSpec(""), UUID(), m_file->dataBuffer())); } llvm::Expected LineEntryTest::GetLineEntryForLine(uint32_t line) { diff --git a/lldb/unittests/TestingSupport/TestUtilities.h b/lldb/unittests/TestingSupport/TestUtilities.h --- a/lldb/unittests/TestingSupport/TestUtilities.h +++ b/lldb/unittests/TestingSupport/TestUtilities.h @@ -9,6 +9,7 @@ #ifndef LLDB_UNITTESTS_TESTINGSUPPORT_TESTUTILITIES_H #define LLDB_UNITTESTS_TESTINGSUPPORT_TESTUTILITIES_H +#include "lldb/Utility/DataBuffer.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Error.h" @@ -31,25 +32,39 @@ class TestFile { public: - static llvm::Expected fromYaml(llvm::StringRef Yaml); - static llvm::Expected fromYamlFile(const llvm::Twine &Name); + static llvm::Expected fromYaml(llvm::StringRef Yaml, + bool InMemory = false); + static llvm::Expected fromYamlFile(const llvm::Twine &Name, + bool InMemory = false); - TestFile(TestFile &&RHS) : Name(std::move(RHS.Name)) { + TestFile(TestFile &&RHS) + : Name(std::move(RHS.Name)), Buffer(std::move(RHS.Buffer)) { RHS.Name = llvm::None; + RHS.Buffer = llvm::None; } ~TestFile(); llvm::StringRef name() { return *Name; } + lldb::DataBufferSP dataBuffer() { + auto *buffer = reinterpret_cast(Buffer->data()); + return std::make_shared(const_cast(buffer), + Buffer->size()); + } + private: TestFile(llvm::StringRef Name, llvm::FileRemover &&Remover) : Name(std::string(Name)) { Remover.releaseFile(); } + + TestFile(std::string &&Buffer) : Buffer(Buffer) {} + void operator=(const TestFile &) = delete; llvm::Optional Name; + llvm::Optional Buffer; }; } diff --git a/lldb/unittests/TestingSupport/TestUtilities.cpp b/lldb/unittests/TestingSupport/TestUtilities.cpp --- a/lldb/unittests/TestingSupport/TestUtilities.cpp +++ b/lldb/unittests/TestingSupport/TestUtilities.cpp @@ -26,9 +26,21 @@ return std::string(result.str()); } -llvm::Expected TestFile::fromYaml(llvm::StringRef Yaml) { +llvm::Expected TestFile::fromYaml(llvm::StringRef Yaml, + bool InMemory) { const auto *Info = testing::UnitTest::GetInstance()->current_test_info(); assert(Info); + + if (InMemory) { + std::string Buffer; + llvm::raw_string_ostream OS(Buffer); + llvm::yaml::Input YIn(Yaml); + if (!llvm::yaml::convertYAML(YIn, OS, [](const llvm::Twine &Msg) {})) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "convertYAML() failed"); + return TestFile(std::move(Buffer)); + } + llvm::SmallString<128> Name; int FD; if (std::error_code EC = llvm::sys::fs::createTemporaryFile( @@ -46,13 +58,14 @@ return TestFile(Name, std::move(Remover)); } -llvm::Expected TestFile::fromYamlFile(const llvm::Twine &Name) { +llvm::Expected TestFile::fromYamlFile(const llvm::Twine &Name, + bool InMemory) { auto BufferOrError = llvm::MemoryBuffer::getFile(GetInputFilePath(Name), /*FileSize*/ -1, /*RequiresNullTerminator*/ false); if (!BufferOrError) return llvm::errorCodeToError(BufferOrError.getError()); - return fromYaml(BufferOrError.get()->getBuffer()); + return fromYaml(BufferOrError.get()->getBuffer(), InMemory); } TestFile::~TestFile() {