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.addResult(Key, Value);
37   }
38 
39   std::vector<std::pair<std::string, std::string>> AllKVResults() override {
40     return Results.AllKVResults();
41   }
42 
43   void forEachResult(llvm::function_ref<void(StringRef Key, StringRef Value)>
44                          Callback) override {
45     Results.forEachResult(Callback);
46   }
47 
48 private:
49   InMemoryToolResults Results;
50   std::mutex Mutex;
51 };
52 
53 } // namespace
54 
55 AllTUsToolExecutor::AllTUsToolExecutor(
56     const CompilationDatabase &Compilations, unsigned ThreadCount,
57     std::shared_ptr<PCHContainerOperations> PCHContainerOps)
58     : Compilations(Compilations), Results(new ThreadSafeToolResults),
59       Context(Results.get()), ThreadCount(ThreadCount) {}
60 
61 AllTUsToolExecutor::AllTUsToolExecutor(
62     CommonOptionsParser Options, unsigned ThreadCount,
63     std::shared_ptr<PCHContainerOperations> PCHContainerOps)
64     : OptionsParser(std::move(Options)),
65       Compilations(OptionsParser->getCompilations()),
66       Results(new ThreadSafeToolResults), Context(Results.get()),
67       ThreadCount(ThreadCount) {}
68 
69 llvm::Error AllTUsToolExecutor::execute(
70     llvm::ArrayRef<
71         std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
72         Actions) {
73   if (Actions.empty())
74     return make_string_error("No action to execute.");
75 
76   if (Actions.size() != 1)
77     return make_string_error(
78         "Only support executing exactly 1 action at this point.");
79 
80   std::string ErrorMsg;
81   std::mutex TUMutex;
82   auto AppendError = [&](llvm::Twine Err) {
83     std::unique_lock<std::mutex> LockGuard(TUMutex);
84     ErrorMsg += Err.str();
85   };
86 
87   auto Log = [&](llvm::Twine Msg) {
88     std::unique_lock<std::mutex> LockGuard(TUMutex);
89     llvm::errs() << Msg.str() << "\n";
90   };
91 
92   auto Files = Compilations.getAllFiles();
93   // Add a counter to track the progress.
94   const std::string TotalNumStr = std::to_string(Files.size());
95   unsigned Counter = 0;
96   auto Count = [&]() {
97     std::unique_lock<std::mutex> LockGuard(TUMutex);
98     return ++Counter;
99   };
100 
101   auto &Action = Actions.front();
102 
103   {
104     llvm::ThreadPool Pool(ThreadCount == 0 ? llvm::hardware_concurrency()
105                                            : ThreadCount);
106 
107     for (std::string File : Files) {
108       Pool.async(
109           [&](std::string Path) {
110             Log("[" + std::to_string(Count()) + "/" + TotalNumStr +
111                 "] Processing file " + Path);
112             ClangTool Tool(Compilations, {Path});
113             Tool.appendArgumentsAdjuster(Action.second);
114             Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters());
115             for (const auto &FileAndContent : OverlayFiles)
116               Tool.mapVirtualFile(FileAndContent.first(),
117                                   FileAndContent.second);
118             if (Tool.run(Action.first.get()))
119               AppendError(llvm::Twine("Failed to run action on ") + Path +
120                           "\n");
121           },
122           File);
123     }
124   }
125 
126   if (!ErrorMsg.empty())
127     return make_string_error(ErrorMsg);
128 
129   return llvm::Error::success();
130 }
131 
132 static llvm::cl::opt<unsigned> ExecutorConcurrency(
133     "execute-concurrency",
134     llvm::cl::desc("The number of threads used to process all files in "
135                    "parallel. Set to 0 for hardware concurrency."),
136     llvm::cl::init(0));
137 
138 class AllTUsToolExecutorPlugin : public ToolExecutorPlugin {
139 public:
140   llvm::Expected<std::unique_ptr<ToolExecutor>>
141   create(CommonOptionsParser &OptionsParser) override {
142     if (OptionsParser.getSourcePathList().empty())
143       return make_string_error(
144           "[AllTUsToolExecutorPlugin] Please provide a directory/file path in "
145           "the compilation database.");
146     return llvm::make_unique<AllTUsToolExecutor>(std::move(OptionsParser),
147                                                  ExecutorConcurrency);
148   }
149 };
150 
151 static ToolExecutorPluginRegistry::Add<AllTUsToolExecutorPlugin>
152     X("all-TUs", "Runs FrontendActions on all TUs in the compilation database. "
153                  "Tool results are stored in memory.");
154 
155 // This anchor is used to force the linker to link in the generated object file
156 // and thus register the plugin.
157 volatile int AllTUsToolExecutorAnchorSource = 0;
158 
159 } // end namespace tooling
160 } // end namespace clang
161