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