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