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/Regex.h"
12#include "llvm/Support/ThreadPool.h"
13#include "llvm/Support/Threading.h"
14#include "llvm/Support/VirtualFileSystem.h"
15
16namespace clang {
17namespace tooling {
18
19const char *AllTUsToolExecutor::ExecutorName = "AllTUsToolExecutor";
20
21namespace {
22llvm::Error make_string_error(const llvm::Twine &Message) {
23 return llvm::make_error<llvm::StringError>(Args: Message,
24 Args: llvm::inconvertibleErrorCode());
25}
26
27ArgumentsAdjuster getDefaultArgumentsAdjusters() {
28 return combineAdjusters(
29 First: getClangStripOutputAdjuster(),
30 Second: combineAdjusters(First: getClangSyntaxOnlyAdjuster(),
31 Second: getClangStripDependencyFileAdjuster()));
32}
33
34class ThreadSafeToolResults : public ToolResults {
35public:
36 void addResult(StringRef Key, StringRef Value) override {
37 std::unique_lock<std::mutex> LockGuard(Mutex);
38 Results.addResult(Key, Value);
39 }
40
41 std::vector<std::pair<llvm::StringRef, llvm::StringRef>>
42 AllKVResults() override {
43 return Results.AllKVResults();
44 }
45
46 void forEachResult(llvm::function_ref<void(StringRef Key, StringRef Value)>
47 Callback) override {
48 Results.forEachResult(Callback);
49 }
50
51private:
52 InMemoryToolResults Results;
53 std::mutex Mutex;
54};
55
56} // namespace
57
58llvm::cl::opt<std::string>
59 Filter("filter",
60 llvm::cl::desc("Only process files that match this filter. "
61 "This flag only applies to all-TUs."),
62 llvm::cl::init(Val: ".*"));
63
64AllTUsToolExecutor::AllTUsToolExecutor(
65 const CompilationDatabase &Compilations, unsigned ThreadCount,
66 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
67 : Compilations(Compilations), Results(new ThreadSafeToolResults),
68 Context(Results.get()), ThreadCount(ThreadCount) {}
69
70AllTUsToolExecutor::AllTUsToolExecutor(
71 CommonOptionsParser Options, unsigned ThreadCount,
72 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
73 : OptionsParser(std::move(Options)),
74 Compilations(OptionsParser->getCompilations()),
75 Results(new ThreadSafeToolResults), Context(Results.get()),
76 ThreadCount(ThreadCount) {}
77
78llvm::Error AllTUsToolExecutor::execute(
79 llvm::ArrayRef<
80 std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
81 Actions) {
82 if (Actions.empty())
83 return make_string_error(Message: "No action to execute.");
84
85 if (Actions.size() != 1)
86 return make_string_error(
87 Message: "Only support executing exactly 1 action at this point.");
88
89 std::string ErrorMsg;
90 std::mutex TUMutex;
91 auto AppendError = [&](llvm::Twine Err) {
92 std::unique_lock<std::mutex> LockGuard(TUMutex);
93 ErrorMsg += Err.str();
94 };
95
96 auto Log = [&](llvm::Twine Msg) {
97 std::unique_lock<std::mutex> LockGuard(TUMutex);
98 llvm::errs() << Msg.str() << "\n";
99 };
100
101 std::vector<std::string> Files;
102 llvm::Regex RegexFilter(Filter);
103 for (const auto& File : Compilations.getAllFiles()) {
104 if (RegexFilter.match(String: File))
105 Files.push_back(x: File);
106 }
107 // Add a counter to track the progress.
108 const std::string TotalNumStr = std::to_string(val: Files.size());
109 unsigned Counter = 0;
110 auto Count = [&]() {
111 std::unique_lock<std::mutex> LockGuard(TUMutex);
112 return ++Counter;
113 };
114
115 auto &Action = Actions.front();
116
117 {
118 llvm::DefaultThreadPool Pool(llvm::hardware_concurrency(ThreadCount));
119 for (std::string File : Files) {
120 Pool.async(
121 F: [&](std::string Path) {
122 Log("[" + std::to_string(val: Count()) + "/" + TotalNumStr +
123 "] Processing file " + Path);
124 // Each thread gets an independent copy of a VFS to allow different
125 // concurrent working directories.
126 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
127 llvm::vfs::createPhysicalFileSystem();
128 ClangTool Tool(Compilations, {Path},
129 std::make_shared<PCHContainerOperations>(), FS);
130 Tool.appendArgumentsAdjuster(Adjuster: Action.second);
131 Tool.appendArgumentsAdjuster(Adjuster: getDefaultArgumentsAdjusters());
132 for (const auto &FileAndContent : OverlayFiles)
133 Tool.mapVirtualFile(FilePath: FileAndContent.first(),
134 Content: FileAndContent.second);
135 if (Tool.run(Action: Action.first.get()))
136 AppendError(llvm::Twine("Failed to run action on ") + Path +
137 "\n");
138 },
139 ArgList&: 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(Message: 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(Val: 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 Message: "[AllTUsToolExecutorPlugin] Please provide a directory/file path in "
165 "the compilation database.");
166 return std::make_unique<AllTUsToolExecutor>(args: std::move(OptionsParser),
167 args&: 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