1 //===- lib/Tooling/AllTUsExecution.cpp - Execute actions on all TUs. ------===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/Tooling/AllTUsExecution.h" 10 #include "clang/Tooling/ToolExecutorPluginRegistry.h" 11 #include "llvm/Support/ThreadPool.h" 12 #include "llvm/Support/VirtualFileSystem.h" 13 14 namespace clang { 15 namespace tooling { 16 17 const char *AllTUsToolExecutor::ExecutorName = "AllTUsToolExecutor"; 18 19 namespace { 20 llvm::Error make_string_error(const llvm::Twine &Message) { 21 return llvm::make_error<llvm::StringError>(Message, 22 llvm::inconvertibleErrorCode()); 23 } 24 25 ArgumentsAdjuster getDefaultArgumentsAdjusters() { 26 return combineAdjusters( 27 getClangStripOutputAdjuster(), 28 combineAdjusters(getClangSyntaxOnlyAdjuster(), 29 getClangStripDependencyFileAdjuster())); 30 } 31 32 class ThreadSafeToolResults : public ToolResults { 33 public: 34 void addResult(StringRef Key, StringRef Value) override { 35 std::unique_lock<std::mutex> LockGuard(Mutex); 36 Results.addResult(Key, Value); 37 } 38 39 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> 40 AllKVResults() override { 41 return Results.AllKVResults(); 42 } 43 44 void forEachResult(llvm::function_ref<void(StringRef Key, StringRef Value)> 45 Callback) override { 46 Results.forEachResult(Callback); 47 } 48 49 private: 50 InMemoryToolResults Results; 51 std::mutex Mutex; 52 }; 53 54 } // namespace 55 56 llvm::cl::opt<std::string> 57 Filter("filter", 58 llvm::cl::desc("Only process files that match this filter. " 59 "This flag only applies to all-TUs."), 60 llvm::cl::init(".*")); 61 62 AllTUsToolExecutor::AllTUsToolExecutor( 63 const CompilationDatabase &Compilations, unsigned ThreadCount, 64 std::shared_ptr<PCHContainerOperations> PCHContainerOps) 65 : Compilations(Compilations), Results(new ThreadSafeToolResults), 66 Context(Results.get()), ThreadCount(ThreadCount) {} 67 68 AllTUsToolExecutor::AllTUsToolExecutor( 69 CommonOptionsParser Options, unsigned ThreadCount, 70 std::shared_ptr<PCHContainerOperations> PCHContainerOps) 71 : OptionsParser(std::move(Options)), 72 Compilations(OptionsParser->getCompilations()), 73 Results(new ThreadSafeToolResults), Context(Results.get()), 74 ThreadCount(ThreadCount) {} 75 76 llvm::Error AllTUsToolExecutor::execute( 77 llvm::ArrayRef< 78 std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>> 79 Actions) { 80 if (Actions.empty()) 81 return make_string_error("No action to execute."); 82 83 if (Actions.size() != 1) 84 return make_string_error( 85 "Only support executing exactly 1 action at this point."); 86 87 std::string ErrorMsg; 88 std::mutex TUMutex; 89 auto AppendError = [&](llvm::Twine Err) { 90 std::unique_lock<std::mutex> LockGuard(TUMutex); 91 ErrorMsg += Err.str(); 92 }; 93 94 auto Log = [&](llvm::Twine Msg) { 95 std::unique_lock<std::mutex> LockGuard(TUMutex); 96 llvm::errs() << Msg.str() << "\n"; 97 }; 98 99 std::vector<std::string> Files; 100 llvm::Regex RegexFilter(Filter); 101 for (const auto& File : Compilations.getAllFiles()) { 102 if (RegexFilter.match(File)) 103 Files.push_back(File); 104 } 105 // Add a counter to track the progress. 106 const std::string TotalNumStr = std::to_string(Files.size()); 107 unsigned Counter = 0; 108 auto Count = [&]() { 109 std::unique_lock<std::mutex> LockGuard(TUMutex); 110 return ++Counter; 111 }; 112 113 auto &Action = Actions.front(); 114 115 { 116 llvm::ThreadPool Pool(ThreadCount == 0 ? llvm::hardware_concurrency() 117 : ThreadCount); 118 for (std::string File : Files) { 119 Pool.async( 120 [&](std::string Path) { 121 Log("[" + std::to_string(Count()) + "/" + TotalNumStr + 122 "] Processing file " + Path); 123 // Each thread gets an indepent copy of a VFS to allow different 124 // concurrent working directories. 125 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = 126 llvm::vfs::createPhysicalFileSystem().release(); 127 ClangTool Tool(Compilations, {Path}, 128 std::make_shared<PCHContainerOperations>(), FS); 129 Tool.appendArgumentsAdjuster(Action.second); 130 Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters()); 131 for (const auto &FileAndContent : OverlayFiles) 132 Tool.mapVirtualFile(FileAndContent.first(), 133 FileAndContent.second); 134 if (Tool.run(Action.first.get())) 135 AppendError(llvm::Twine("Failed to run action on ") + Path + 136 "\n"); 137 }, 138 File); 139 } 140 // Make sure all tasks have finished before resetting the working directory. 141 Pool.wait(); 142 } 143 144 if (!ErrorMsg.empty()) 145 return make_string_error(ErrorMsg); 146 147 return llvm::Error::success(); 148 } 149 150 static llvm::cl::opt<unsigned> ExecutorConcurrency( 151 "execute-concurrency", 152 llvm::cl::desc("The number of threads used to process all files in " 153 "parallel. Set to 0 for hardware concurrency. " 154 "This flag only applies to all-TUs."), 155 llvm::cl::init(0)); 156 157 class AllTUsToolExecutorPlugin : public ToolExecutorPlugin { 158 public: 159 llvm::Expected<std::unique_ptr<ToolExecutor>> 160 create(CommonOptionsParser &OptionsParser) override { 161 if (OptionsParser.getSourcePathList().empty()) 162 return make_string_error( 163 "[AllTUsToolExecutorPlugin] Please provide a directory/file path in " 164 "the compilation database."); 165 return llvm::make_unique<AllTUsToolExecutor>(std::move(OptionsParser), 166 ExecutorConcurrency); 167 } 168 }; 169 170 static ToolExecutorPluginRegistry::Add<AllTUsToolExecutorPlugin> 171 X("all-TUs", "Runs FrontendActions on all TUs in the compilation database. " 172 "Tool results are stored in memory."); 173 174 // This anchor is used to force the linker to link in the generated object file 175 // and thus register the plugin. 176 volatile int AllTUsToolExecutorAnchorSource = 0; 177 178 } // end namespace tooling 179 } // end namespace clang 180