1 //===- lib/Tooling/AllTUsExecution.cpp - Execute actions on all TUs. ------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "clang/Tooling/AllTUsExecution.h" 11 #include "clang/Tooling/ToolExecutorPluginRegistry.h" 12 #include "llvm/Support/ThreadPool.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 llvm::SmallString<128> InitialWorkingDir; 119 if (auto EC = llvm::sys::fs::current_path(InitialWorkingDir)) { 120 InitialWorkingDir = ""; 121 llvm::errs() << "Error while getting current working directory: " 122 << EC.message() << "\n"; 123 } 124 for (std::string File : Files) { 125 Pool.async( 126 [&](std::string Path) { 127 Log("[" + std::to_string(Count()) + "/" + TotalNumStr + 128 "] Processing file " + Path); 129 ClangTool Tool(Compilations, {Path}); 130 Tool.appendArgumentsAdjuster(Action.second); 131 Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters()); 132 for (const auto &FileAndContent : OverlayFiles) 133 Tool.mapVirtualFile(FileAndContent.first(), 134 FileAndContent.second); 135 // Do not restore working dir from multiple threads to avoid races. 136 Tool.setRestoreWorkingDir(false); 137 if (Tool.run(Action.first.get())) 138 AppendError(llvm::Twine("Failed to run action on ") + Path + 139 "\n"); 140 }, 141 File); 142 } 143 // Make sure all tasks have finished before resetting the working directory. 144 Pool.wait(); 145 if (!InitialWorkingDir.empty()) { 146 if (auto EC = llvm::sys::fs::set_current_path(InitialWorkingDir)) 147 llvm::errs() << "Error while restoring working directory: " 148 << EC.message() << "\n"; 149 } 150 } 151 152 if (!ErrorMsg.empty()) 153 return make_string_error(ErrorMsg); 154 155 return llvm::Error::success(); 156 } 157 158 static llvm::cl::opt<unsigned> ExecutorConcurrency( 159 "execute-concurrency", 160 llvm::cl::desc("The number of threads used to process all files in " 161 "parallel. Set to 0 for hardware concurrency. " 162 "This flag only applies to all-TUs."), 163 llvm::cl::init(0)); 164 165 class AllTUsToolExecutorPlugin : public ToolExecutorPlugin { 166 public: 167 llvm::Expected<std::unique_ptr<ToolExecutor>> 168 create(CommonOptionsParser &OptionsParser) override { 169 if (OptionsParser.getSourcePathList().empty()) 170 return make_string_error( 171 "[AllTUsToolExecutorPlugin] Please provide a directory/file path in " 172 "the compilation database."); 173 return llvm::make_unique<AllTUsToolExecutor>(std::move(OptionsParser), 174 ExecutorConcurrency); 175 } 176 }; 177 178 static ToolExecutorPluginRegistry::Add<AllTUsToolExecutorPlugin> 179 X("all-TUs", "Runs FrontendActions on all TUs in the compilation database. " 180 "Tool results are stored in memory."); 181 182 // This anchor is used to force the linker to link in the generated object file 183 // and thus register the plugin. 184 volatile int AllTUsToolExecutorAnchorSource = 0; 185 186 } // end namespace tooling 187 } // end namespace clang 188