1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | #include "clang/Tooling/AllTUsExecution.h" |
10 | #include "clang/Tooling/ToolExecutorPluginRegistry.h" |
11 | #include "llvm/Support/ThreadPool.h" |
12 | #include "llvm/Support/VirtualFileSystem.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<llvm::StringRef, llvm::StringRef>> |
40 | AllKVResults() override { |
41 | return Results.AllKVResults(); |
42 | } |
43 | |
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 | } |
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 | |
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 | |
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 | |
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 | |
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 | for (std::string File : Files) { |
119 | Pool.async( |
120 | [&](std::string Path) { |
121 | Log("[" + std::to_string(Count()) + "/" + TotalNumStr + |
122 | "] Processing file " + Path); |
123 | |
124 | |
125 | IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = |
126 | llvm::vfs::createPhysicalFileSystem().release(); |
127 | ClangTool Tool(Compilations, {Path}, |
128 | std::make_shared<PCHContainerOperations>(), FS); |
129 | Tool.appendArgumentsAdjuster(Action.second); |
130 | Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters()); |
131 | for (const auto &FileAndContent : OverlayFiles) |
132 | Tool.mapVirtualFile(FileAndContent.first(), |
133 | FileAndContent.second); |
134 | if (Tool.run(Action.first.get())) |
135 | AppendError(llvm::Twine("Failed to run action on ") + Path + |
136 | "\n"); |
137 | }, |
138 | File); |
139 | } |
140 | |
141 | Pool.wait(); |
142 | } |
143 | |
144 | if (!ErrorMsg.empty()) |
145 | return make_string_error(ErrorMsg); |
146 | |
147 | return llvm::Error::success(); |
148 | } |
149 | |
150 | static llvm::cl::opt<unsigned> ExecutorConcurrency( |
151 | "execute-concurrency", |
152 | llvm::cl::desc("The number of threads used to process all files in " |
153 | "parallel. Set to 0 for hardware concurrency. " |
154 | "This flag only applies to all-TUs."), |
155 | llvm::cl::init(0)); |
156 | |
157 | class AllTUsToolExecutorPlugin : public ToolExecutorPlugin { |
158 | public: |
159 | llvm::Expected<std::unique_ptr<ToolExecutor>> |
160 | create(CommonOptionsParser &OptionsParser) override { |
161 | if (OptionsParser.getSourcePathList().empty()) |
162 | return make_string_error( |
163 | "[AllTUsToolExecutorPlugin] Please provide a directory/file path in " |
164 | "the compilation database."); |
165 | return llvm::make_unique<AllTUsToolExecutor>(std::move(OptionsParser), |
166 | ExecutorConcurrency); |
167 | } |
168 | }; |
169 | |
170 | static ToolExecutorPluginRegistry::Add<AllTUsToolExecutorPlugin> |
171 | X("all-TUs", "Runs FrontendActions on all TUs in the compilation database. " |
172 | "Tool results are stored in memory."); |
173 | |
174 | |
175 | |
176 | volatile int AllTUsToolExecutorAnchorSource = 0; |
177 | |
178 | } |
179 | } |
180 | |