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