Clang Project

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