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