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