10b57cec5SDimitry Andric //===- lib/Tooling/AllTUsExecution.cpp - Execute actions on all TUs. ------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "clang/Tooling/AllTUsExecution.h" 100b57cec5SDimitry Andric #include "clang/Tooling/ToolExecutorPluginRegistry.h" 115ffd83dbSDimitry Andric #include "llvm/Support/Regex.h" 120b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h" 135ffd83dbSDimitry Andric #include "llvm/Support/Threading.h" 140b57cec5SDimitry Andric #include "llvm/Support/VirtualFileSystem.h" 150b57cec5SDimitry Andric 160b57cec5SDimitry Andric namespace clang { 170b57cec5SDimitry Andric namespace tooling { 180b57cec5SDimitry Andric 190b57cec5SDimitry Andric const char *AllTUsToolExecutor::ExecutorName = "AllTUsToolExecutor"; 200b57cec5SDimitry Andric 210b57cec5SDimitry Andric namespace { 220b57cec5SDimitry Andric llvm::Error make_string_error(const llvm::Twine &Message) { 230b57cec5SDimitry Andric return llvm::make_error<llvm::StringError>(Message, 240b57cec5SDimitry Andric llvm::inconvertibleErrorCode()); 250b57cec5SDimitry Andric } 260b57cec5SDimitry Andric 270b57cec5SDimitry Andric ArgumentsAdjuster getDefaultArgumentsAdjusters() { 280b57cec5SDimitry Andric return combineAdjusters( 290b57cec5SDimitry Andric getClangStripOutputAdjuster(), 300b57cec5SDimitry Andric combineAdjusters(getClangSyntaxOnlyAdjuster(), 310b57cec5SDimitry Andric getClangStripDependencyFileAdjuster())); 320b57cec5SDimitry Andric } 330b57cec5SDimitry Andric 340b57cec5SDimitry Andric class ThreadSafeToolResults : public ToolResults { 350b57cec5SDimitry Andric public: 360b57cec5SDimitry Andric void addResult(StringRef Key, StringRef Value) override { 370b57cec5SDimitry Andric std::unique_lock<std::mutex> LockGuard(Mutex); 380b57cec5SDimitry Andric Results.addResult(Key, Value); 390b57cec5SDimitry Andric } 400b57cec5SDimitry Andric 410b57cec5SDimitry Andric std::vector<std::pair<llvm::StringRef, llvm::StringRef>> 420b57cec5SDimitry Andric AllKVResults() override { 430b57cec5SDimitry Andric return Results.AllKVResults(); 440b57cec5SDimitry Andric } 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric void forEachResult(llvm::function_ref<void(StringRef Key, StringRef Value)> 470b57cec5SDimitry Andric Callback) override { 480b57cec5SDimitry Andric Results.forEachResult(Callback); 490b57cec5SDimitry Andric } 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric private: 520b57cec5SDimitry Andric InMemoryToolResults Results; 530b57cec5SDimitry Andric std::mutex Mutex; 540b57cec5SDimitry Andric }; 550b57cec5SDimitry Andric 560b57cec5SDimitry Andric } // namespace 570b57cec5SDimitry Andric 580b57cec5SDimitry Andric llvm::cl::opt<std::string> 590b57cec5SDimitry Andric Filter("filter", 600b57cec5SDimitry Andric llvm::cl::desc("Only process files that match this filter. " 610b57cec5SDimitry Andric "This flag only applies to all-TUs."), 620b57cec5SDimitry Andric llvm::cl::init(".*")); 630b57cec5SDimitry Andric 640b57cec5SDimitry Andric AllTUsToolExecutor::AllTUsToolExecutor( 650b57cec5SDimitry Andric const CompilationDatabase &Compilations, unsigned ThreadCount, 660b57cec5SDimitry Andric std::shared_ptr<PCHContainerOperations> PCHContainerOps) 670b57cec5SDimitry Andric : Compilations(Compilations), Results(new ThreadSafeToolResults), 680b57cec5SDimitry Andric Context(Results.get()), ThreadCount(ThreadCount) {} 690b57cec5SDimitry Andric 700b57cec5SDimitry Andric AllTUsToolExecutor::AllTUsToolExecutor( 710b57cec5SDimitry Andric CommonOptionsParser Options, unsigned ThreadCount, 720b57cec5SDimitry Andric std::shared_ptr<PCHContainerOperations> PCHContainerOps) 730b57cec5SDimitry Andric : OptionsParser(std::move(Options)), 740b57cec5SDimitry Andric Compilations(OptionsParser->getCompilations()), 750b57cec5SDimitry Andric Results(new ThreadSafeToolResults), Context(Results.get()), 760b57cec5SDimitry Andric ThreadCount(ThreadCount) {} 770b57cec5SDimitry Andric 780b57cec5SDimitry Andric llvm::Error AllTUsToolExecutor::execute( 790b57cec5SDimitry Andric llvm::ArrayRef< 800b57cec5SDimitry Andric std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>> 810b57cec5SDimitry Andric Actions) { 820b57cec5SDimitry Andric if (Actions.empty()) 830b57cec5SDimitry Andric return make_string_error("No action to execute."); 840b57cec5SDimitry Andric 850b57cec5SDimitry Andric if (Actions.size() != 1) 860b57cec5SDimitry Andric return make_string_error( 870b57cec5SDimitry Andric "Only support executing exactly 1 action at this point."); 880b57cec5SDimitry Andric 890b57cec5SDimitry Andric std::string ErrorMsg; 900b57cec5SDimitry Andric std::mutex TUMutex; 910b57cec5SDimitry Andric auto AppendError = [&](llvm::Twine Err) { 920b57cec5SDimitry Andric std::unique_lock<std::mutex> LockGuard(TUMutex); 930b57cec5SDimitry Andric ErrorMsg += Err.str(); 940b57cec5SDimitry Andric }; 950b57cec5SDimitry Andric 960b57cec5SDimitry Andric auto Log = [&](llvm::Twine Msg) { 970b57cec5SDimitry Andric std::unique_lock<std::mutex> LockGuard(TUMutex); 980b57cec5SDimitry Andric llvm::errs() << Msg.str() << "\n"; 990b57cec5SDimitry Andric }; 1000b57cec5SDimitry Andric 1010b57cec5SDimitry Andric std::vector<std::string> Files; 1020b57cec5SDimitry Andric llvm::Regex RegexFilter(Filter); 1030b57cec5SDimitry Andric for (const auto& File : Compilations.getAllFiles()) { 1040b57cec5SDimitry Andric if (RegexFilter.match(File)) 1050b57cec5SDimitry Andric Files.push_back(File); 1060b57cec5SDimitry Andric } 1070b57cec5SDimitry Andric // Add a counter to track the progress. 1080b57cec5SDimitry Andric const std::string TotalNumStr = std::to_string(Files.size()); 1090b57cec5SDimitry Andric unsigned Counter = 0; 1100b57cec5SDimitry Andric auto Count = [&]() { 1110b57cec5SDimitry Andric std::unique_lock<std::mutex> LockGuard(TUMutex); 1120b57cec5SDimitry Andric return ++Counter; 1130b57cec5SDimitry Andric }; 1140b57cec5SDimitry Andric 1150b57cec5SDimitry Andric auto &Action = Actions.front(); 1160b57cec5SDimitry Andric 1170b57cec5SDimitry Andric { 118*0fca6ea1SDimitry Andric llvm::DefaultThreadPool Pool(llvm::hardware_concurrency(ThreadCount)); 1190b57cec5SDimitry Andric for (std::string File : Files) { 1200b57cec5SDimitry Andric Pool.async( 1210b57cec5SDimitry Andric [&](std::string Path) { 1220b57cec5SDimitry Andric Log("[" + std::to_string(Count()) + "/" + TotalNumStr + 1230b57cec5SDimitry Andric "] Processing file " + Path); 124bdd1243dSDimitry Andric // Each thread gets an independent copy of a VFS to allow different 1250b57cec5SDimitry Andric // concurrent working directories. 1260b57cec5SDimitry Andric IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = 127e8d8bef9SDimitry Andric llvm::vfs::createPhysicalFileSystem(); 1280b57cec5SDimitry Andric ClangTool Tool(Compilations, {Path}, 1290b57cec5SDimitry Andric std::make_shared<PCHContainerOperations>(), FS); 1300b57cec5SDimitry Andric Tool.appendArgumentsAdjuster(Action.second); 1310b57cec5SDimitry Andric Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters()); 1320b57cec5SDimitry Andric for (const auto &FileAndContent : OverlayFiles) 1330b57cec5SDimitry Andric Tool.mapVirtualFile(FileAndContent.first(), 1340b57cec5SDimitry Andric FileAndContent.second); 1350b57cec5SDimitry Andric if (Tool.run(Action.first.get())) 1360b57cec5SDimitry Andric AppendError(llvm::Twine("Failed to run action on ") + Path + 1370b57cec5SDimitry Andric "\n"); 1380b57cec5SDimitry Andric }, 1390b57cec5SDimitry Andric File); 1400b57cec5SDimitry Andric } 1410b57cec5SDimitry Andric // Make sure all tasks have finished before resetting the working directory. 1420b57cec5SDimitry Andric Pool.wait(); 1430b57cec5SDimitry Andric } 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric if (!ErrorMsg.empty()) 1460b57cec5SDimitry Andric return make_string_error(ErrorMsg); 1470b57cec5SDimitry Andric 1480b57cec5SDimitry Andric return llvm::Error::success(); 1490b57cec5SDimitry Andric } 1500b57cec5SDimitry Andric 151a7dea167SDimitry Andric llvm::cl::opt<unsigned> ExecutorConcurrency( 1520b57cec5SDimitry Andric "execute-concurrency", 1530b57cec5SDimitry Andric llvm::cl::desc("The number of threads used to process all files in " 1540b57cec5SDimitry Andric "parallel. Set to 0 for hardware concurrency. " 1550b57cec5SDimitry Andric "This flag only applies to all-TUs."), 1560b57cec5SDimitry Andric llvm::cl::init(0)); 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric class AllTUsToolExecutorPlugin : public ToolExecutorPlugin { 1590b57cec5SDimitry Andric public: 1600b57cec5SDimitry Andric llvm::Expected<std::unique_ptr<ToolExecutor>> 1610b57cec5SDimitry Andric create(CommonOptionsParser &OptionsParser) override { 1620b57cec5SDimitry Andric if (OptionsParser.getSourcePathList().empty()) 1630b57cec5SDimitry Andric return make_string_error( 1640b57cec5SDimitry Andric "[AllTUsToolExecutorPlugin] Please provide a directory/file path in " 1650b57cec5SDimitry Andric "the compilation database."); 166a7dea167SDimitry Andric return std::make_unique<AllTUsToolExecutor>(std::move(OptionsParser), 1670b57cec5SDimitry Andric ExecutorConcurrency); 1680b57cec5SDimitry Andric } 1690b57cec5SDimitry Andric }; 1700b57cec5SDimitry Andric 1710b57cec5SDimitry Andric static ToolExecutorPluginRegistry::Add<AllTUsToolExecutorPlugin> 1720b57cec5SDimitry Andric X("all-TUs", "Runs FrontendActions on all TUs in the compilation database. " 1730b57cec5SDimitry Andric "Tool results are stored in memory."); 1740b57cec5SDimitry Andric 1750b57cec5SDimitry Andric // This anchor is used to force the linker to link in the generated object file 1760b57cec5SDimitry Andric // and thus register the plugin. 1770b57cec5SDimitry Andric volatile int AllTUsToolExecutorAnchorSource = 0; 1780b57cec5SDimitry Andric 1790b57cec5SDimitry Andric } // end namespace tooling 1800b57cec5SDimitry Andric } // end namespace clang 181