AllTUsExecution.cpp 6.2 KB

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