Index: lldb/include/lldb/Host/FileSystem.h =================================================================== --- lldb/include/lldb/Host/FileSystem.h +++ lldb/include/lldb/Host/FileSystem.h @@ -11,12 +11,12 @@ #include "lldb/Host/File.h" #include "lldb/Utility/DataBufferLLVM.h" +#include "lldb/Utility/FileCollector.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Status.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/Chrono.h" -#include "llvm/Support/FileCollector.h" #include "llvm/Support/VirtualFileSystem.h" #include "lldb/lldb-types.h" @@ -34,7 +34,7 @@ FileSystem() : m_fs(llvm::vfs::getRealFileSystem()), m_collector(nullptr), m_mapped(false) {} - FileSystem(std::shared_ptr collector) + FileSystem(std::shared_ptr collector) : m_fs(llvm::vfs::getRealFileSystem()), m_collector(collector), m_mapped(false) {} FileSystem(llvm::IntrusiveRefCntPtr fs, @@ -47,7 +47,7 @@ static FileSystem &Instance(); static void Initialize(); - static void Initialize(std::shared_ptr collector); + static void Initialize(std::shared_ptr collector); static llvm::Error Initialize(const FileSpec &mapping); static void Initialize(llvm::IntrusiveRefCntPtr fs); static void Terminate(); @@ -189,10 +189,12 @@ void Collect(const FileSpec &file_spec); void Collect(const llvm::Twine &file); + FileCollector *GetCollector() { return m_collector.get(); } + private: static llvm::Optional &InstanceImpl(); llvm::IntrusiveRefCntPtr m_fs; - std::shared_ptr m_collector; + std::shared_ptr m_collector; bool m_mapped; }; } // namespace lldb_private Index: lldb/include/lldb/Utility/FileCollector.h =================================================================== --- /dev/null +++ lldb/include/lldb/Utility/FileCollector.h @@ -0,0 +1,62 @@ +//===-- FileCollector.h -----------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDD_SUPPORT_FILE_COLLECTOR_H +#define LLDD_SUPPORT_FILE_COLLECTOR_H + +#include "llvm/Support/FileCollector.h" + +#include + +namespace lldb_private { + +class FileCollector : public llvm::FileCollector { +public: + FileCollector(std::string Root, std::string OverlayRoot) + : llvm::FileCollector(Root, OverlayRoot) {} + + void addFile(const llvm::Twine &file) override; + + enum class Action { + KeepContents, + Ignore, + }; + + enum class Matcher { + Full, + Substring, + Extension, + }; + + void registerMatcher(llvm::StringRef name, Action action, Matcher matcher); + +protected: + /// Returns an Action if any of the matchers match the given component. None + /// otherwise. + llvm::Optional getAction(llvm::StringRef component); + + /// Returns true if the given path matches any of the matchers. Returns false + /// otherwise. + bool match(const llvm::Twine &file); + + struct MatchEntry { + std::string name; + Action action; + Matcher matcher; + + MatchEntry(std::string name, Action action, Matcher matcher) + : name(std::move(name)), action(action), matcher(matcher) {} + bool matches(llvm::StringRef component); + }; + + std::vector m_matchers; +}; + +} // namespace lldb_private + +#endif // LLVM_SUPPORT_FILE_COLLECTOR_H Index: lldb/include/lldb/Utility/Reproducer.h =================================================================== --- lldb/include/lldb/Utility/Reproducer.h +++ lldb/include/lldb/Utility/Reproducer.h @@ -9,10 +9,10 @@ #ifndef LLDB_UTILITY_REPRODUCER_H #define LLDB_UTILITY_REPRODUCER_H +#include "lldb/Utility/FileCollector.h" #include "lldb/Utility/FileSpec.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/Error.h" -#include "llvm/Support/FileCollector.h" #include "llvm/Support/YAMLTraits.h" #include @@ -90,13 +90,11 @@ FileProvider(const FileSpec &directory) : Provider(directory), - m_collector(std::make_shared( + m_collector(std::make_shared( directory.CopyByAppendingPathComponent("root").GetPath(), directory.GetPath())) {} - std::shared_ptr GetFileCollector() { - return m_collector; - } + std::shared_ptr GetFileCollector() { return m_collector; } void Keep() override { auto mapping = GetRoot().CopyByAppendingPathComponent(Info::file); @@ -109,7 +107,7 @@ static char ID; private: - std::shared_ptr m_collector; + std::shared_ptr m_collector; }; /// Provider for the LLDB version number. Index: lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp =================================================================== --- lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp +++ lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp @@ -73,6 +73,10 @@ void SymbolVendorMacOSX::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); + if (FileCollector *collector = FileSystem::Instance().GetCollector()) { + collector->registerMatcher(".dsym", FileCollector::Action::KeepContents, + FileCollector::Matcher::Extension); + } } void SymbolVendorMacOSX::Terminate() { Index: lldb/source/Utility/CMakeLists.txt =================================================================== --- lldb/source/Utility/CMakeLists.txt +++ lldb/source/Utility/CMakeLists.txt @@ -24,6 +24,7 @@ DataExtractor.cpp Environment.cpp Event.cpp + FileCollector.cpp FileSpec.cpp GDBRemote.cpp IOObject.cpp Index: lldb/source/Utility/FileCollector.cpp =================================================================== --- /dev/null +++ lldb/source/Utility/FileCollector.cpp @@ -0,0 +1,74 @@ +//===-- FileCollector.cpp ---------------------------------------*- C++ -*-===// +// +// 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 "lldb/Utility/FileCollector.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" + +using namespace lldb_private; + +void FileCollector::addFile(const llvm::Twine &file) { + if (match(file)) + return; + + // Add the file. + addFileImpl(file.str()); +} + +void FileCollector::registerMatcher(llvm::StringRef name, + FileCollector::Action action, + FileCollector::Matcher matcher) { + m_matchers.emplace_back(name.lower(), action, matcher); +} + +bool FileCollector::MatchEntry::matches(llvm::StringRef component) { + switch (matcher) { + case Matcher::Full: + return component.lower() == name; + case Matcher::Substring: + return component.lower().find(name) != std::string::npos; + case Matcher::Extension: + return llvm::sys::path::extension(component).lower() == name; + }; + return false; +} + +llvm::Optional +FileCollector::getAction(llvm::StringRef component) { + // Iterate in reverse order so new matchers can override previously + // registered ones. + for (MatchEntry entry : llvm::reverse(m_matchers)) { + if (entry.matches(component)) + return entry.action; + } + return {}; +} + +bool FileCollector::match(const llvm::Twine &file) { + std::string path = file.str(); + auto begin = llvm::sys::path::begin(path); + auto end = llvm::sys::path::end(path); + llvm::SmallString<128> buffer; + for (auto it = begin; it != end; ++it) { + if (llvm::Optional action = getAction(*it)) { + switch (*action) { + case Action::KeepContents: + llvm::sys::path::append(buffer, begin, ++it); + addDirectory(buffer.str()); + break; + case Action::Ignore: + break; + } + return true; + } + } + + return false; +} Index: lldb/test/Shell/Reproducer/TestDSYM.test =================================================================== --- /dev/null +++ lldb/test/Shell/Reproducer/TestDSYM.test @@ -0,0 +1,11 @@ +# REQUIRES: system-darwin +# Ensure that the reproducers captures the whole dSYM bundle. + +# RUN: rm -rf %t.repro +# RUN: %clang_host %S/Inputs/simple.c -g -o %t.out +# RUN: touch %t.out.dSYM/foo.bar + +# RUN: %lldb -x -b --capture --capture-path %t.repro %t.out -o 'b main' -o 'run' -o 'reproducer generate' + +# RUN: %lldb -b -o 'reproducer dump -p files -f %t.repro' | FileCheck %s --check-prefix FILES +# FILES: foo.bar Index: lldb/unittests/Utility/CMakeLists.txt =================================================================== --- lldb/unittests/Utility/CMakeLists.txt +++ lldb/unittests/Utility/CMakeLists.txt @@ -10,6 +10,7 @@ EnvironmentTest.cpp EventTest.cpp FileSpecTest.cpp + FileCollectorTest.cpp FlagsTest.cpp ListenerTest.cpp LogTest.cpp Index: lldb/unittests/Utility/FileCollectorTest.cpp =================================================================== --- /dev/null +++ lldb/unittests/Utility/FileCollectorTest.cpp @@ -0,0 +1,112 @@ +//===-- ReproducerTest.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 "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "lldb/Utility/FileCollector.h" + +using namespace lldb_private; + +class TestFileCollector : public FileCollector { +public: + TestFileCollector() : FileCollector("", ""){}; + + using FileCollector::getAction; + void addDirectory(const llvm::Twine &file) override { + directories.emplace_back(file.str()); + } + std::vector directories; +}; + +TEST(FileCollectorTest, GetActionTest) { + { + TestFileCollector collector; + collector.registerMatcher("foo", FileCollector::Action::KeepContents, + FileCollector::Matcher::Full); + + auto action = collector.getAction("foo"); + ASSERT_TRUE(static_cast(action)); + EXPECT_EQ(FileCollector::Action::KeepContents, *action); + + action = collector.getAction("bar"); + ASSERT_FALSE(static_cast(action)); + + action = collector.getAction("foobar"); + ASSERT_FALSE(static_cast(action)); + + // Register a new matcher that overrides the previous one. + collector.registerMatcher("foo", FileCollector::Action::KeepContents, + FileCollector::Matcher::Substring); + + action = collector.getAction("foo"); + ASSERT_TRUE(static_cast(action)); + EXPECT_EQ(FileCollector::Action::KeepContents, *action); + + action = collector.getAction("bar"); + ASSERT_FALSE(static_cast(action)); + + action = collector.getAction("foobar"); + ASSERT_TRUE(static_cast(action)); + EXPECT_EQ(FileCollector::Action::KeepContents, *action); + } + + { + TestFileCollector collector; + collector.registerMatcher(".foo", FileCollector::Action::KeepContents, + FileCollector::Matcher::Extension); + + auto action = collector.getAction("foo"); + ASSERT_FALSE(static_cast(action)); + + action = collector.getAction("bar"); + ASSERT_FALSE(static_cast(action)); + + action = collector.getAction("bar.foo"); + ASSERT_TRUE(static_cast(action)); + EXPECT_EQ(FileCollector::Action::KeepContents, *action); + + action = collector.getAction("foo.bar"); + ASSERT_FALSE(static_cast(action)); + } + + { + TestFileCollector collector; + collector.registerMatcher(".foo", FileCollector::Action::Ignore, + FileCollector::Matcher::Extension); + + auto action = collector.getAction("bar.foo"); + ASSERT_TRUE(static_cast(action)); + EXPECT_EQ(FileCollector::Action::Ignore, *action); + } +} + +TEST(FileCollectorTest, AddFileTest) { + { + TestFileCollector collector; + collector.registerMatcher("foo", FileCollector::Action::KeepContents, + FileCollector::Matcher::Full); + collector.addFile("/path/to/foo/bar/"); + ASSERT_EQ(static_cast(1), collector.directories.size()); + EXPECT_EQ("/path/to/foo", collector.directories.back()); + + collector.registerMatcher("foo", FileCollector::Action::KeepContents, + FileCollector::Matcher::Substring); + collector.addFile("/path/to/foobar/"); + ASSERT_EQ(static_cast(2), collector.directories.size()); + EXPECT_EQ("/path/to/foobar", collector.directories.back()); + } + + { + TestFileCollector collector; + collector.registerMatcher("foo", FileCollector::Action::Ignore, + FileCollector::Matcher::Full); + collector.addFile("/path/to/foo/bar/"); + ASSERT_EQ(static_cast(0), collector.directories.size()); + } +} Index: llvm/include/llvm/Support/FileCollector.h =================================================================== --- llvm/include/llvm/Support/FileCollector.h +++ llvm/include/llvm/Support/FileCollector.h @@ -45,7 +45,7 @@ createCollectorVFS(IntrusiveRefCntPtr BaseFS, std::shared_ptr Collector); -private: +protected: void addFileImpl(StringRef SrcPath); bool markAsSeen(StringRef Path) { @@ -60,7 +60,6 @@ VFSWriter.addFileMapping(VirtualPath, RealPath); } -protected: /// Synchronizes adding files. std::mutex Mutex; Index: llvm/lib/Support/FileCollector.cpp =================================================================== --- llvm/lib/Support/FileCollector.cpp +++ llvm/lib/Support/FileCollector.cpp @@ -61,26 +61,25 @@ return true; } -void FileCollector::addFile(const Twine &file) { - std::lock_guard lock(Mutex); - std::string FileStr = file.str(); - if (markAsSeen(FileStr)) - addFileImpl(FileStr); -} +void FileCollector::addFile(const Twine &file) { addFileImpl(file.str()); } void FileCollector::addDirectory(const Twine &dir) { assert(sys::fs::is_directory(dir)); std::error_code EC; sys::fs::recursive_directory_iterator Iter(dir, EC); sys::fs::recursive_directory_iterator End; - addFile(dir); // Add the directory in case it's empty. + addFileImpl(dir.str()); // Add the directory in case it's empty. for (; Iter != End && !EC; Iter.increment(EC)) { if (sys::fs::is_regular_file(Iter->path())) - addFile(Iter->path()); + addFileImpl(Iter->path()); } } void FileCollector::addFileImpl(StringRef SrcPath) { + std::lock_guard lock(Mutex); + if (!markAsSeen(SrcPath)) + return; + // We need an absolute src path to append to the root. SmallString<256> AbsoluteSrc = SrcPath; sys::fs::make_absolute(AbsoluteSrc);