xref: /freebsd-src/contrib/llvm-project/clang/lib/Tooling/AllTUsExecution.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===- lib/Tooling/AllTUsExecution.cpp - Execute actions on all TUs. ------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "clang/Tooling/AllTUsExecution.h"
100b57cec5SDimitry Andric #include "clang/Tooling/ToolExecutorPluginRegistry.h"
115ffd83dbSDimitry Andric #include "llvm/Support/Regex.h"
120b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h"
135ffd83dbSDimitry Andric #include "llvm/Support/Threading.h"
140b57cec5SDimitry Andric #include "llvm/Support/VirtualFileSystem.h"
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric namespace clang {
170b57cec5SDimitry Andric namespace tooling {
180b57cec5SDimitry Andric 
190b57cec5SDimitry Andric const char *AllTUsToolExecutor::ExecutorName = "AllTUsToolExecutor";
200b57cec5SDimitry Andric 
210b57cec5SDimitry Andric namespace {
220b57cec5SDimitry Andric llvm::Error make_string_error(const llvm::Twine &Message) {
230b57cec5SDimitry Andric   return llvm::make_error<llvm::StringError>(Message,
240b57cec5SDimitry Andric                                              llvm::inconvertibleErrorCode());
250b57cec5SDimitry Andric }
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric ArgumentsAdjuster getDefaultArgumentsAdjusters() {
280b57cec5SDimitry Andric   return combineAdjusters(
290b57cec5SDimitry Andric       getClangStripOutputAdjuster(),
300b57cec5SDimitry Andric       combineAdjusters(getClangSyntaxOnlyAdjuster(),
310b57cec5SDimitry Andric                        getClangStripDependencyFileAdjuster()));
320b57cec5SDimitry Andric }
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric class ThreadSafeToolResults : public ToolResults {
350b57cec5SDimitry Andric public:
360b57cec5SDimitry Andric   void addResult(StringRef Key, StringRef Value) override {
370b57cec5SDimitry Andric     std::unique_lock<std::mutex> LockGuard(Mutex);
380b57cec5SDimitry Andric     Results.addResult(Key, Value);
390b57cec5SDimitry Andric   }
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric   std::vector<std::pair<llvm::StringRef, llvm::StringRef>>
420b57cec5SDimitry Andric   AllKVResults() override {
430b57cec5SDimitry Andric     return Results.AllKVResults();
440b57cec5SDimitry Andric   }
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric   void forEachResult(llvm::function_ref<void(StringRef Key, StringRef Value)>
470b57cec5SDimitry Andric                          Callback) override {
480b57cec5SDimitry Andric     Results.forEachResult(Callback);
490b57cec5SDimitry Andric   }
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric private:
520b57cec5SDimitry Andric   InMemoryToolResults Results;
530b57cec5SDimitry Andric   std::mutex Mutex;
540b57cec5SDimitry Andric };
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric } // namespace
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric llvm::cl::opt<std::string>
590b57cec5SDimitry Andric     Filter("filter",
600b57cec5SDimitry Andric            llvm::cl::desc("Only process files that match this filter. "
610b57cec5SDimitry Andric                           "This flag only applies to all-TUs."),
620b57cec5SDimitry Andric            llvm::cl::init(".*"));
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric AllTUsToolExecutor::AllTUsToolExecutor(
650b57cec5SDimitry Andric     const CompilationDatabase &Compilations, unsigned ThreadCount,
660b57cec5SDimitry Andric     std::shared_ptr<PCHContainerOperations> PCHContainerOps)
670b57cec5SDimitry Andric     : Compilations(Compilations), Results(new ThreadSafeToolResults),
680b57cec5SDimitry Andric       Context(Results.get()), ThreadCount(ThreadCount) {}
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric AllTUsToolExecutor::AllTUsToolExecutor(
710b57cec5SDimitry Andric     CommonOptionsParser Options, unsigned ThreadCount,
720b57cec5SDimitry Andric     std::shared_ptr<PCHContainerOperations> PCHContainerOps)
730b57cec5SDimitry Andric     : OptionsParser(std::move(Options)),
740b57cec5SDimitry Andric       Compilations(OptionsParser->getCompilations()),
750b57cec5SDimitry Andric       Results(new ThreadSafeToolResults), Context(Results.get()),
760b57cec5SDimitry Andric       ThreadCount(ThreadCount) {}
770b57cec5SDimitry Andric 
780b57cec5SDimitry Andric llvm::Error AllTUsToolExecutor::execute(
790b57cec5SDimitry Andric     llvm::ArrayRef<
800b57cec5SDimitry Andric         std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
810b57cec5SDimitry Andric         Actions) {
820b57cec5SDimitry Andric   if (Actions.empty())
830b57cec5SDimitry Andric     return make_string_error("No action to execute.");
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric   if (Actions.size() != 1)
860b57cec5SDimitry Andric     return make_string_error(
870b57cec5SDimitry Andric         "Only support executing exactly 1 action at this point.");
880b57cec5SDimitry Andric 
890b57cec5SDimitry Andric   std::string ErrorMsg;
900b57cec5SDimitry Andric   std::mutex TUMutex;
910b57cec5SDimitry Andric   auto AppendError = [&](llvm::Twine Err) {
920b57cec5SDimitry Andric     std::unique_lock<std::mutex> LockGuard(TUMutex);
930b57cec5SDimitry Andric     ErrorMsg += Err.str();
940b57cec5SDimitry Andric   };
950b57cec5SDimitry Andric 
960b57cec5SDimitry Andric   auto Log = [&](llvm::Twine Msg) {
970b57cec5SDimitry Andric     std::unique_lock<std::mutex> LockGuard(TUMutex);
980b57cec5SDimitry Andric     llvm::errs() << Msg.str() << "\n";
990b57cec5SDimitry Andric   };
1000b57cec5SDimitry Andric 
1010b57cec5SDimitry Andric   std::vector<std::string> Files;
1020b57cec5SDimitry Andric   llvm::Regex RegexFilter(Filter);
1030b57cec5SDimitry Andric   for (const auto& File : Compilations.getAllFiles()) {
1040b57cec5SDimitry Andric     if (RegexFilter.match(File))
1050b57cec5SDimitry Andric       Files.push_back(File);
1060b57cec5SDimitry Andric   }
1070b57cec5SDimitry Andric   // Add a counter to track the progress.
1080b57cec5SDimitry Andric   const std::string TotalNumStr = std::to_string(Files.size());
1090b57cec5SDimitry Andric   unsigned Counter = 0;
1100b57cec5SDimitry Andric   auto Count = [&]() {
1110b57cec5SDimitry Andric     std::unique_lock<std::mutex> LockGuard(TUMutex);
1120b57cec5SDimitry Andric     return ++Counter;
1130b57cec5SDimitry Andric   };
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   auto &Action = Actions.front();
1160b57cec5SDimitry Andric 
1170b57cec5SDimitry Andric   {
118*0fca6ea1SDimitry Andric     llvm::DefaultThreadPool Pool(llvm::hardware_concurrency(ThreadCount));
1190b57cec5SDimitry Andric     for (std::string File : Files) {
1200b57cec5SDimitry Andric       Pool.async(
1210b57cec5SDimitry Andric           [&](std::string Path) {
1220b57cec5SDimitry Andric             Log("[" + std::to_string(Count()) + "/" + TotalNumStr +
1230b57cec5SDimitry Andric                 "] Processing file " + Path);
124bdd1243dSDimitry Andric             // Each thread gets an independent copy of a VFS to allow different
1250b57cec5SDimitry Andric             // concurrent working directories.
1260b57cec5SDimitry Andric             IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
127e8d8bef9SDimitry Andric                 llvm::vfs::createPhysicalFileSystem();
1280b57cec5SDimitry Andric             ClangTool Tool(Compilations, {Path},
1290b57cec5SDimitry Andric                            std::make_shared<PCHContainerOperations>(), FS);
1300b57cec5SDimitry Andric             Tool.appendArgumentsAdjuster(Action.second);
1310b57cec5SDimitry Andric             Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters());
1320b57cec5SDimitry Andric             for (const auto &FileAndContent : OverlayFiles)
1330b57cec5SDimitry Andric               Tool.mapVirtualFile(FileAndContent.first(),
1340b57cec5SDimitry Andric                                   FileAndContent.second);
1350b57cec5SDimitry Andric             if (Tool.run(Action.first.get()))
1360b57cec5SDimitry Andric               AppendError(llvm::Twine("Failed to run action on ") + Path +
1370b57cec5SDimitry Andric                           "\n");
1380b57cec5SDimitry Andric           },
1390b57cec5SDimitry Andric           File);
1400b57cec5SDimitry Andric     }
1410b57cec5SDimitry Andric     // Make sure all tasks have finished before resetting the working directory.
1420b57cec5SDimitry Andric     Pool.wait();
1430b57cec5SDimitry Andric   }
1440b57cec5SDimitry Andric 
1450b57cec5SDimitry Andric   if (!ErrorMsg.empty())
1460b57cec5SDimitry Andric     return make_string_error(ErrorMsg);
1470b57cec5SDimitry Andric 
1480b57cec5SDimitry Andric   return llvm::Error::success();
1490b57cec5SDimitry Andric }
1500b57cec5SDimitry Andric 
151a7dea167SDimitry Andric llvm::cl::opt<unsigned> ExecutorConcurrency(
1520b57cec5SDimitry Andric     "execute-concurrency",
1530b57cec5SDimitry Andric     llvm::cl::desc("The number of threads used to process all files in "
1540b57cec5SDimitry Andric                    "parallel. Set to 0 for hardware concurrency. "
1550b57cec5SDimitry Andric                    "This flag only applies to all-TUs."),
1560b57cec5SDimitry Andric     llvm::cl::init(0));
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric class AllTUsToolExecutorPlugin : public ToolExecutorPlugin {
1590b57cec5SDimitry Andric public:
1600b57cec5SDimitry Andric   llvm::Expected<std::unique_ptr<ToolExecutor>>
1610b57cec5SDimitry Andric   create(CommonOptionsParser &OptionsParser) override {
1620b57cec5SDimitry Andric     if (OptionsParser.getSourcePathList().empty())
1630b57cec5SDimitry Andric       return make_string_error(
1640b57cec5SDimitry Andric           "[AllTUsToolExecutorPlugin] Please provide a directory/file path in "
1650b57cec5SDimitry Andric           "the compilation database.");
166a7dea167SDimitry Andric     return std::make_unique<AllTUsToolExecutor>(std::move(OptionsParser),
1670b57cec5SDimitry Andric                                                  ExecutorConcurrency);
1680b57cec5SDimitry Andric   }
1690b57cec5SDimitry Andric };
1700b57cec5SDimitry Andric 
1710b57cec5SDimitry Andric static ToolExecutorPluginRegistry::Add<AllTUsToolExecutorPlugin>
1720b57cec5SDimitry Andric     X("all-TUs", "Runs FrontendActions on all TUs in the compilation database. "
1730b57cec5SDimitry Andric                  "Tool results are stored in memory.");
1740b57cec5SDimitry Andric 
1750b57cec5SDimitry Andric // This anchor is used to force the linker to link in the generated object file
1760b57cec5SDimitry Andric // and thus register the plugin.
1770b57cec5SDimitry Andric volatile int AllTUsToolExecutorAnchorSource = 0;
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric } // end namespace tooling
1800b57cec5SDimitry Andric } // end namespace clang
181