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