//===- lib/Tooling/AllTUsExecution.cpp - Execute actions on all TUs. ------===// // // 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 "clang/Tooling/AllTUsExecution.h" #include "clang/Tooling/ToolExecutorPluginRegistry.h" #include "llvm/Support/Regex.h" #include "llvm/Support/ThreadPool.h" #include "llvm/Support/Threading.h" #include "llvm/Support/VirtualFileSystem.h" namespace clang { namespace tooling { const char *AllTUsToolExecutor::ExecutorName = "AllTUsToolExecutor"; namespace { llvm::Error make_string_error(const llvm::Twine &Message) { return llvm::make_error(Message, llvm::inconvertibleErrorCode()); } ArgumentsAdjuster getDefaultArgumentsAdjusters() { return combineAdjusters( getClangStripOutputAdjuster(), combineAdjusters(getClangSyntaxOnlyAdjuster(), getClangStripDependencyFileAdjuster())); } class ThreadSafeToolResults : public ToolResults { public: void addResult(StringRef Key, StringRef Value) override { std::unique_lock LockGuard(Mutex); Results.addResult(Key, Value); } std::vector> AllKVResults() override { return Results.AllKVResults(); } void forEachResult(llvm::function_ref Callback) override { Results.forEachResult(Callback); } private: InMemoryToolResults Results; std::mutex Mutex; }; } // namespace llvm::cl::opt Filter("filter", llvm::cl::desc("Only process files that match this filter. " "This flag only applies to all-TUs."), llvm::cl::init(".*")); AllTUsToolExecutor::AllTUsToolExecutor( const CompilationDatabase &Compilations, unsigned ThreadCount, std::shared_ptr PCHContainerOps) : Compilations(Compilations), Results(new ThreadSafeToolResults), Context(Results.get()), ThreadCount(ThreadCount) {} AllTUsToolExecutor::AllTUsToolExecutor( CommonOptionsParser Options, unsigned ThreadCount, std::shared_ptr PCHContainerOps) : OptionsParser(std::move(Options)), Compilations(OptionsParser->getCompilations()), Results(new ThreadSafeToolResults), Context(Results.get()), ThreadCount(ThreadCount) {} llvm::Error AllTUsToolExecutor::execute( llvm::ArrayRef< std::pair, ArgumentsAdjuster>> Actions) { if (Actions.empty()) return make_string_error("No action to execute."); if (Actions.size() != 1) return make_string_error( "Only support executing exactly 1 action at this point."); std::string ErrorMsg; std::mutex TUMutex; auto AppendError = [&](llvm::Twine Err) { std::unique_lock LockGuard(TUMutex); ErrorMsg += Err.str(); }; auto Log = [&](llvm::Twine Msg) { std::unique_lock LockGuard(TUMutex); llvm::errs() << Msg.str() << "\n"; }; std::vector Files; llvm::Regex RegexFilter(Filter); for (const auto& File : Compilations.getAllFiles()) { if (RegexFilter.match(File)) Files.push_back(File); } // Add a counter to track the progress. const std::string TotalNumStr = std::to_string(Files.size()); unsigned Counter = 0; auto Count = [&]() { std::unique_lock LockGuard(TUMutex); return ++Counter; }; auto &Action = Actions.front(); { llvm::ThreadPool Pool(llvm::hardware_concurrency(ThreadCount)); for (std::string File : Files) { Pool.async( [&](std::string Path) { Log("[" + std::to_string(Count()) + "/" + TotalNumStr + "] Processing file " + Path); // Each thread gets an independent copy of a VFS to allow different // concurrent working directories. IntrusiveRefCntPtr FS = llvm::vfs::createPhysicalFileSystem(); ClangTool Tool(Compilations, {Path}, std::make_shared(), FS); Tool.appendArgumentsAdjuster(Action.second); Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters()); for (const auto &FileAndContent : OverlayFiles) Tool.mapVirtualFile(FileAndContent.first(), FileAndContent.second); if (Tool.run(Action.first.get())) AppendError(llvm::Twine("Failed to run action on ") + Path + "\n"); }, File); } // Make sure all tasks have finished before resetting the working directory. Pool.wait(); } if (!ErrorMsg.empty()) return make_string_error(ErrorMsg); return llvm::Error::success(); } llvm::cl::opt ExecutorConcurrency( "execute-concurrency", llvm::cl::desc("The number of threads used to process all files in " "parallel. Set to 0 for hardware concurrency. " "This flag only applies to all-TUs."), llvm::cl::init(0)); class AllTUsToolExecutorPlugin : public ToolExecutorPlugin { public: llvm::Expected> create(CommonOptionsParser &OptionsParser) override { if (OptionsParser.getSourcePathList().empty()) return make_string_error( "[AllTUsToolExecutorPlugin] Please provide a directory/file path in " "the compilation database."); return std::make_unique(std::move(OptionsParser), ExecutorConcurrency); } }; static ToolExecutorPluginRegistry::Add X("all-TUs", "Runs FrontendActions on all TUs in the compilation database. " "Tool results are stored in memory."); // This anchor is used to force the linker to link in the generated object file // and thus register the plugin. volatile int AllTUsToolExecutorAnchorSource = 0; } // end namespace tooling } // end namespace clang