1 | //===- lib/Tooling/AllTUsExecution.cpp - Execute actions on all TUs. ------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "clang/Tooling/AllTUsExecution.h" |
10 | #include "clang/Tooling/ToolExecutorPluginRegistry.h" |
11 | #include "llvm/Support/Regex.h" |
12 | #include "llvm/Support/ThreadPool.h" |
13 | #include "llvm/Support/Threading.h" |
14 | #include "llvm/Support/VirtualFileSystem.h" |
15 | |
16 | namespace clang { |
17 | namespace tooling { |
18 | |
19 | const char *AllTUsToolExecutor::ExecutorName = "AllTUsToolExecutor" ; |
20 | |
21 | namespace { |
22 | llvm::Error make_string_error(const llvm::Twine &Message) { |
23 | return llvm::make_error<llvm::StringError>(Args: Message, |
24 | Args: llvm::inconvertibleErrorCode()); |
25 | } |
26 | |
27 | ArgumentsAdjuster getDefaultArgumentsAdjusters() { |
28 | return combineAdjusters( |
29 | First: getClangStripOutputAdjuster(), |
30 | Second: combineAdjusters(First: getClangSyntaxOnlyAdjuster(), |
31 | Second: getClangStripDependencyFileAdjuster())); |
32 | } |
33 | |
34 | class ThreadSafeToolResults : public ToolResults { |
35 | public: |
36 | void addResult(StringRef Key, StringRef Value) override { |
37 | std::unique_lock<std::mutex> LockGuard(Mutex); |
38 | Results.addResult(Key, Value); |
39 | } |
40 | |
41 | std::vector<std::pair<llvm::StringRef, llvm::StringRef>> |
42 | AllKVResults() override { |
43 | return Results.AllKVResults(); |
44 | } |
45 | |
46 | void forEachResult(llvm::function_ref<void(StringRef Key, StringRef Value)> |
47 | Callback) override { |
48 | Results.forEachResult(Callback); |
49 | } |
50 | |
51 | private: |
52 | InMemoryToolResults Results; |
53 | std::mutex Mutex; |
54 | }; |
55 | |
56 | } // namespace |
57 | |
58 | llvm::cl::opt<std::string> |
59 | Filter("filter" , |
60 | llvm::cl::desc("Only process files that match this filter. " |
61 | "This flag only applies to all-TUs." ), |
62 | llvm::cl::init(Val: ".*" )); |
63 | |
64 | AllTUsToolExecutor::AllTUsToolExecutor( |
65 | const CompilationDatabase &Compilations, unsigned ThreadCount, |
66 | std::shared_ptr<PCHContainerOperations> PCHContainerOps) |
67 | : Compilations(Compilations), Results(new ThreadSafeToolResults), |
68 | Context(Results.get()), ThreadCount(ThreadCount) {} |
69 | |
70 | AllTUsToolExecutor::AllTUsToolExecutor( |
71 | CommonOptionsParser Options, unsigned ThreadCount, |
72 | std::shared_ptr<PCHContainerOperations> PCHContainerOps) |
73 | : OptionsParser(std::move(Options)), |
74 | Compilations(OptionsParser->getCompilations()), |
75 | Results(new ThreadSafeToolResults), Context(Results.get()), |
76 | ThreadCount(ThreadCount) {} |
77 | |
78 | llvm::Error AllTUsToolExecutor::execute( |
79 | llvm::ArrayRef< |
80 | std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>> |
81 | Actions) { |
82 | if (Actions.empty()) |
83 | return make_string_error(Message: "No action to execute." ); |
84 | |
85 | if (Actions.size() != 1) |
86 | return make_string_error( |
87 | Message: "Only support executing exactly 1 action at this point." ); |
88 | |
89 | std::string ErrorMsg; |
90 | std::mutex TUMutex; |
91 | auto AppendError = [&](llvm::Twine Err) { |
92 | std::unique_lock<std::mutex> LockGuard(TUMutex); |
93 | ErrorMsg += Err.str(); |
94 | }; |
95 | |
96 | auto Log = [&](llvm::Twine Msg) { |
97 | std::unique_lock<std::mutex> LockGuard(TUMutex); |
98 | llvm::errs() << Msg.str() << "\n" ; |
99 | }; |
100 | |
101 | std::vector<std::string> Files; |
102 | llvm::Regex RegexFilter(Filter); |
103 | for (const auto& File : Compilations.getAllFiles()) { |
104 | if (RegexFilter.match(String: File)) |
105 | Files.push_back(x: File); |
106 | } |
107 | // Add a counter to track the progress. |
108 | const std::string TotalNumStr = std::to_string(val: Files.size()); |
109 | unsigned Counter = 0; |
110 | auto Count = [&]() { |
111 | std::unique_lock<std::mutex> LockGuard(TUMutex); |
112 | return ++Counter; |
113 | }; |
114 | |
115 | auto &Action = Actions.front(); |
116 | |
117 | { |
118 | llvm::DefaultThreadPool Pool(llvm::hardware_concurrency(ThreadCount)); |
119 | for (std::string File : Files) { |
120 | Pool.async( |
121 | F: [&](std::string Path) { |
122 | Log("[" + std::to_string(val: Count()) + "/" + TotalNumStr + |
123 | "] Processing file " + Path); |
124 | // Each thread gets an independent copy of a VFS to allow different |
125 | // concurrent working directories. |
126 | IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = |
127 | llvm::vfs::createPhysicalFileSystem(); |
128 | ClangTool Tool(Compilations, {Path}, |
129 | std::make_shared<PCHContainerOperations>(), FS); |
130 | Tool.appendArgumentsAdjuster(Adjuster: Action.second); |
131 | Tool.appendArgumentsAdjuster(Adjuster: getDefaultArgumentsAdjusters()); |
132 | for (const auto &FileAndContent : OverlayFiles) |
133 | Tool.mapVirtualFile(FilePath: FileAndContent.first(), |
134 | Content: FileAndContent.second); |
135 | if (Tool.run(Action: Action.first.get())) |
136 | AppendError(llvm::Twine("Failed to run action on " ) + Path + |
137 | "\n" ); |
138 | }, |
139 | ArgList&: File); |
140 | } |
141 | // Make sure all tasks have finished before resetting the working directory. |
142 | Pool.wait(); |
143 | } |
144 | |
145 | if (!ErrorMsg.empty()) |
146 | return make_string_error(Message: ErrorMsg); |
147 | |
148 | return llvm::Error::success(); |
149 | } |
150 | |
151 | llvm::cl::opt<unsigned> ExecutorConcurrency( |
152 | "execute-concurrency" , |
153 | llvm::cl::desc("The number of threads used to process all files in " |
154 | "parallel. Set to 0 for hardware concurrency. " |
155 | "This flag only applies to all-TUs." ), |
156 | llvm::cl::init(Val: 0)); |
157 | |
158 | class AllTUsToolExecutorPlugin : public ToolExecutorPlugin { |
159 | public: |
160 | llvm::Expected<std::unique_ptr<ToolExecutor>> |
161 | create(CommonOptionsParser &OptionsParser) override { |
162 | if (OptionsParser.getSourcePathList().empty()) |
163 | return make_string_error( |
164 | Message: "[AllTUsToolExecutorPlugin] Please provide a directory/file path in " |
165 | "the compilation database." ); |
166 | return std::make_unique<AllTUsToolExecutor>(args: std::move(OptionsParser), |
167 | args&: ExecutorConcurrency); |
168 | } |
169 | }; |
170 | |
171 | static ToolExecutorPluginRegistry::Add<AllTUsToolExecutorPlugin> |
172 | X("all-TUs" , "Runs FrontendActions on all TUs in the compilation database. " |
173 | "Tool results are stored in memory." ); |
174 | |
175 | // This anchor is used to force the linker to link in the generated object file |
176 | // and thus register the plugin. |
177 | volatile int AllTUsToolExecutorAnchorSource = 0; |
178 | |
179 | } // end namespace tooling |
180 | } // end namespace clang |
181 | |