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 {
make_string_error(const llvm::Twine & Message)20 llvm::Error make_string_error(const llvm::Twine &Message) {
21 return llvm::make_error<llvm::StringError>(Message,
22 llvm::inconvertibleErrorCode());
23 }
24
getDefaultArgumentsAdjusters()25 ArgumentsAdjuster getDefaultArgumentsAdjusters() {
26 return combineAdjusters(
27 getClangStripOutputAdjuster(),
28 combineAdjusters(getClangSyntaxOnlyAdjuster(),
29 getClangStripDependencyFileAdjuster()));
30 }
31
32 class ThreadSafeToolResults : public ToolResults {
33 public:
addResult(StringRef Key,StringRef Value)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<llvm::StringRef, llvm::StringRef>>
AllKVResults()40 AllKVResults() override {
41 return Results.AllKVResults();
42 }
43
forEachResult(llvm::function_ref<void (StringRef Key,StringRef Value)> Callback)44 void forEachResult(llvm::function_ref<void(StringRef Key, StringRef Value)>
45 Callback) override {
46 Results.forEachResult(Callback);
47 }
48
49 private:
50 InMemoryToolResults Results;
51 std::mutex Mutex;
52 };
53
54 } // namespace
55
56 llvm::cl::opt<std::string>
57 Filter("filter",
58 llvm::cl::desc("Only process files that match this filter. "
59 "This flag only applies to all-TUs."),
60 llvm::cl::init(".*"));
61
AllTUsToolExecutor(const CompilationDatabase & Compilations,unsigned ThreadCount,std::shared_ptr<PCHContainerOperations> PCHContainerOps)62 AllTUsToolExecutor::AllTUsToolExecutor(
63 const CompilationDatabase &Compilations, unsigned ThreadCount,
64 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
65 : Compilations(Compilations), Results(new ThreadSafeToolResults),
66 Context(Results.get()), ThreadCount(ThreadCount) {}
67
AllTUsToolExecutor(CommonOptionsParser Options,unsigned ThreadCount,std::shared_ptr<PCHContainerOperations> PCHContainerOps)68 AllTUsToolExecutor::AllTUsToolExecutor(
69 CommonOptionsParser Options, unsigned ThreadCount,
70 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
71 : OptionsParser(std::move(Options)),
72 Compilations(OptionsParser->getCompilations()),
73 Results(new ThreadSafeToolResults), Context(Results.get()),
74 ThreadCount(ThreadCount) {}
75
execute(llvm::ArrayRef<std::pair<std::unique_ptr<FrontendActionFactory>,ArgumentsAdjuster>> Actions)76 llvm::Error AllTUsToolExecutor::execute(
77 llvm::ArrayRef<
78 std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
79 Actions) {
80 if (Actions.empty())
81 return make_string_error("No action to execute.");
82
83 if (Actions.size() != 1)
84 return make_string_error(
85 "Only support executing exactly 1 action at this point.");
86
87 std::string ErrorMsg;
88 std::mutex TUMutex;
89 auto AppendError = [&](llvm::Twine Err) {
90 std::unique_lock<std::mutex> LockGuard(TUMutex);
91 ErrorMsg += Err.str();
92 };
93
94 auto Log = [&](llvm::Twine Msg) {
95 std::unique_lock<std::mutex> LockGuard(TUMutex);
96 llvm::errs() << Msg.str() << "\n";
97 };
98
99 std::vector<std::string> Files;
100 llvm::Regex RegexFilter(Filter);
101 for (const auto& File : Compilations.getAllFiles()) {
102 if (RegexFilter.match(File))
103 Files.push_back(File);
104 }
105 // Add a counter to track the progress.
106 const std::string TotalNumStr = std::to_string(Files.size());
107 unsigned Counter = 0;
108 auto Count = [&]() {
109 std::unique_lock<std::mutex> LockGuard(TUMutex);
110 return ++Counter;
111 };
112
113 auto &Action = Actions.front();
114
115 {
116 llvm::ThreadPool Pool(ThreadCount == 0 ? llvm::hardware_concurrency()
117 : ThreadCount);
118 llvm::SmallString<128> InitialWorkingDir;
119 if (auto EC = llvm::sys::fs::current_path(InitialWorkingDir)) {
120 InitialWorkingDir = "";
121 llvm::errs() << "Error while getting current working directory: "
122 << EC.message() << "\n";
123 }
124 for (std::string File : Files) {
125 Pool.async(
126 [&](std::string Path) {
127 Log("[" + std::to_string(Count()) + "/" + TotalNumStr +
128 "] Processing file " + Path);
129 ClangTool Tool(Compilations, {Path});
130 Tool.appendArgumentsAdjuster(Action.second);
131 Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters());
132 for (const auto &FileAndContent : OverlayFiles)
133 Tool.mapVirtualFile(FileAndContent.first(),
134 FileAndContent.second);
135 // Do not restore working dir from multiple threads to avoid races.
136 Tool.setRestoreWorkingDir(false);
137 if (Tool.run(Action.first.get()))
138 AppendError(llvm::Twine("Failed to run action on ") + Path +
139 "\n");
140 },
141 File);
142 }
143 // Make sure all tasks have finished before resetting the working directory.
144 Pool.wait();
145 if (!InitialWorkingDir.empty()) {
146 if (auto EC = llvm::sys::fs::set_current_path(InitialWorkingDir))
147 llvm::errs() << "Error while restoring working directory: "
148 << EC.message() << "\n";
149 }
150 }
151
152 if (!ErrorMsg.empty())
153 return make_string_error(ErrorMsg);
154
155 return llvm::Error::success();
156 }
157
158 static llvm::cl::opt<unsigned> ExecutorConcurrency(
159 "execute-concurrency",
160 llvm::cl::desc("The number of threads used to process all files in "
161 "parallel. Set to 0 for hardware concurrency. "
162 "This flag only applies to all-TUs."),
163 llvm::cl::init(0));
164
165 class AllTUsToolExecutorPlugin : public ToolExecutorPlugin {
166 public:
167 llvm::Expected<std::unique_ptr<ToolExecutor>>
create(CommonOptionsParser & OptionsParser)168 create(CommonOptionsParser &OptionsParser) override {
169 if (OptionsParser.getSourcePathList().empty())
170 return make_string_error(
171 "[AllTUsToolExecutorPlugin] Please provide a directory/file path in "
172 "the compilation database.");
173 return llvm::make_unique<AllTUsToolExecutor>(std::move(OptionsParser),
174 ExecutorConcurrency);
175 }
176 };
177
178 static ToolExecutorPluginRegistry::Add<AllTUsToolExecutorPlugin>
179 X("all-TUs", "Runs FrontendActions on all TUs in the compilation database. "
180 "Tool results are stored in memory.");
181
182 // This anchor is used to force the linker to link in the generated object file
183 // and thus register the plugin.
184 volatile int AllTUsToolExecutorAnchorSource = 0;
185
186 } // end namespace tooling
187 } // end namespace clang
188