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[Key] = Value; 37 } 38 39 std::vector<std::pair<std::string, std::string>> AllKVResults() override { 40 std::vector<std::pair<std::string, std::string>> KVs; 41 for (const auto &Pair : Results) 42 KVs.emplace_back(Pair.first().str(), Pair.second); 43 return KVs; 44 } 45 46 void forEachResult(llvm::function_ref<void(StringRef Key, StringRef Value)> 47 Callback) override { 48 for (const auto &Pair : Results) 49 Callback(Pair.first(), Pair.second); 50 } 51 52 private: 53 llvm::StringMap<std::string> Results; 54 std::mutex Mutex; 55 }; 56 57 } // namespace 58 59 AllTUsToolExecutor::AllTUsToolExecutor( 60 const CompilationDatabase &Compilations, unsigned ThreadCount, 61 std::shared_ptr<PCHContainerOperations> PCHContainerOps) 62 : Compilations(Compilations), Results(new ThreadSafeToolResults), 63 Context(Results.get()), ThreadCount(ThreadCount) {} 64 65 AllTUsToolExecutor::AllTUsToolExecutor( 66 CommonOptionsParser Options, unsigned ThreadCount, 67 std::shared_ptr<PCHContainerOperations> PCHContainerOps) 68 : OptionsParser(std::move(Options)), 69 Compilations(OptionsParser->getCompilations()), 70 Results(new ThreadSafeToolResults), Context(Results.get()), 71 ThreadCount(ThreadCount) {} 72 73 llvm::Error AllTUsToolExecutor::execute( 74 llvm::ArrayRef< 75 std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>> 76 Actions) { 77 if (Actions.empty()) 78 return make_string_error("No action to execute."); 79 80 if (Actions.size() != 1) 81 return make_string_error( 82 "Only support executing exactly 1 action at this point."); 83 84 std::string ErrorMsg; 85 std::mutex TUMutex; 86 auto AppendError = [&](llvm::Twine Err) { 87 std::unique_lock<std::mutex> LockGuard(TUMutex); 88 ErrorMsg += Err.str(); 89 }; 90 91 auto Log = [&](llvm::Twine Msg) { 92 std::unique_lock<std::mutex> LockGuard(TUMutex); 93 llvm::errs() << Msg.str() << "\n"; 94 }; 95 96 auto Files = Compilations.getAllFiles(); 97 // Add a counter to track the progress. 98 const std::string TotalNumStr = std::to_string(Files.size()); 99 unsigned Counter = 0; 100 auto Count = [&]() { 101 std::unique_lock<std::mutex> LockGuard(TUMutex); 102 return ++Counter; 103 }; 104 105 auto &Action = Actions.front(); 106 107 { 108 llvm::ThreadPool Pool(ThreadCount == 0 ? llvm::hardware_concurrency() 109 : ThreadCount); 110 111 for (std::string File : Files) { 112 Pool.async( 113 [&](std::string Path) { 114 Log("[" + std::to_string(Count()) + "/" + TotalNumStr + 115 "] Processing file " + Path); 116 ClangTool Tool(Compilations, {Path}); 117 Tool.appendArgumentsAdjuster(Action.second); 118 Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters()); 119 for (const auto &FileAndContent : OverlayFiles) 120 Tool.mapVirtualFile(FileAndContent.first(), 121 FileAndContent.second); 122 if (Tool.run(Action.first.get())) 123 AppendError(llvm::Twine("Failed to run action on ") + Path + 124 "\n"); 125 }, 126 File); 127 } 128 } 129 130 if (!ErrorMsg.empty()) 131 return make_string_error(ErrorMsg); 132 133 return llvm::Error::success(); 134 } 135 136 static llvm::cl::opt<unsigned> ExecutorConcurrency( 137 "execute-concurrency", 138 llvm::cl::desc("The number of threads used to process all files in " 139 "parallel. Set to 0 for hardware concurrency."), 140 llvm::cl::init(0)); 141 142 class AllTUsToolExecutorPlugin : public ToolExecutorPlugin { 143 public: 144 llvm::Expected<std::unique_ptr<ToolExecutor>> 145 create(CommonOptionsParser &OptionsParser) override { 146 if (OptionsParser.getSourcePathList().empty()) 147 return make_string_error( 148 "[AllTUsToolExecutorPlugin] Please provide a directory/file path in " 149 "the compilation database."); 150 return llvm::make_unique<AllTUsToolExecutor>(std::move(OptionsParser), 151 ExecutorConcurrency); 152 } 153 }; 154 155 static ToolExecutorPluginRegistry::Add<AllTUsToolExecutorPlugin> 156 X("all-TUs", 157 "Runs FrontendActions on all TUs in the compilation database. " 158 "Tool results are deduplicated by the result key and stored in memory."); 159 160 // This anchor is used to force the linker to link in the generated object file 161 // and thus register the plugin. 162 volatile int AllTUsToolExecutorAnchorSource = 0; 163 164 } // end namespace tooling 165 } // end namespace clang 166