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