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/Regex.h"
  11. #include "llvm/Support/ThreadPool.h"
  12. #include "llvm/Support/Threading.h"
  13. #include "llvm/Support/VirtualFileSystem.h"
  14. namespace clang {
  15. namespace tooling {
  16. const char *AllTUsToolExecutor::ExecutorName = "AllTUsToolExecutor";
  17. namespace {
  18. llvm::Error make_string_error(const llvm::Twine &Message) {
  19. return llvm::make_error<llvm::StringError>(Message,
  20. llvm::inconvertibleErrorCode());
  21. }
  22. ArgumentsAdjuster getDefaultArgumentsAdjusters() {
  23. return combineAdjusters(
  24. getClangStripOutputAdjuster(),
  25. combineAdjusters(getClangSyntaxOnlyAdjuster(),
  26. getClangStripDependencyFileAdjuster()));
  27. }
  28. class ThreadSafeToolResults : public ToolResults {
  29. public:
  30. void addResult(StringRef Key, StringRef Value) override {
  31. std::unique_lock<std::mutex> LockGuard(Mutex);
  32. Results.addResult(Key, Value);
  33. }
  34. std::vector<std::pair<llvm::StringRef, llvm::StringRef>>
  35. AllKVResults() override {
  36. return Results.AllKVResults();
  37. }
  38. void forEachResult(llvm::function_ref<void(StringRef Key, StringRef Value)>
  39. Callback) override {
  40. Results.forEachResult(Callback);
  41. }
  42. private:
  43. InMemoryToolResults Results;
  44. std::mutex Mutex;
  45. };
  46. } // namespace
  47. llvm::cl::opt<std::string>
  48. Filter("filter",
  49. llvm::cl::desc("Only process files that match this filter. "
  50. "This flag only applies to all-TUs."),
  51. llvm::cl::init(".*"));
  52. AllTUsToolExecutor::AllTUsToolExecutor(
  53. const CompilationDatabase &Compilations, unsigned ThreadCount,
  54. std::shared_ptr<PCHContainerOperations> PCHContainerOps)
  55. : Compilations(Compilations), Results(new ThreadSafeToolResults),
  56. Context(Results.get()), ThreadCount(ThreadCount) {}
  57. AllTUsToolExecutor::AllTUsToolExecutor(
  58. CommonOptionsParser Options, unsigned ThreadCount,
  59. std::shared_ptr<PCHContainerOperations> PCHContainerOps)
  60. : OptionsParser(std::move(Options)),
  61. Compilations(OptionsParser->getCompilations()),
  62. Results(new ThreadSafeToolResults), Context(Results.get()),
  63. ThreadCount(ThreadCount) {}
  64. llvm::Error AllTUsToolExecutor::execute(
  65. llvm::ArrayRef<
  66. std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
  67. Actions) {
  68. if (Actions.empty())
  69. return make_string_error("No action to execute.");
  70. if (Actions.size() != 1)
  71. return make_string_error(
  72. "Only support executing exactly 1 action at this point.");
  73. std::string ErrorMsg;
  74. std::mutex TUMutex;
  75. auto AppendError = [&](llvm::Twine Err) {
  76. std::unique_lock<std::mutex> LockGuard(TUMutex);
  77. ErrorMsg += Err.str();
  78. };
  79. auto Log = [&](llvm::Twine Msg) {
  80. std::unique_lock<std::mutex> LockGuard(TUMutex);
  81. llvm::errs() << Msg.str() << "\n";
  82. };
  83. std::vector<std::string> Files;
  84. llvm::Regex RegexFilter(Filter);
  85. for (const auto& File : Compilations.getAllFiles()) {
  86. if (RegexFilter.match(File))
  87. Files.push_back(File);
  88. }
  89. // Add a counter to track the progress.
  90. const std::string TotalNumStr = std::to_string(Files.size());
  91. unsigned Counter = 0;
  92. auto Count = [&]() {
  93. std::unique_lock<std::mutex> LockGuard(TUMutex);
  94. return ++Counter;
  95. };
  96. auto &Action = Actions.front();
  97. {
  98. llvm::ThreadPool Pool(llvm::hardware_concurrency(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 independent copy of a VFS to allow different
  105. // concurrent working directories.
  106. IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
  107. llvm::vfs::createPhysicalFileSystem();
  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