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