/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Serialization/ModuleManager.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- ModuleManager.cpp - Module Manager ---------------------------------===// |
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 | | // This file defines the ModuleManager class, which manages a set of loaded |
10 | | // modules for the ASTReader. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | #include "clang/Serialization/ModuleManager.h" |
15 | | #include "clang/Basic/FileManager.h" |
16 | | #include "clang/Basic/LLVM.h" |
17 | | #include "clang/Lex/HeaderSearch.h" |
18 | | #include "clang/Lex/ModuleMap.h" |
19 | | #include "clang/Serialization/GlobalModuleIndex.h" |
20 | | #include "clang/Serialization/InMemoryModuleCache.h" |
21 | | #include "clang/Serialization/ModuleFile.h" |
22 | | #include "clang/Serialization/PCHContainerOperations.h" |
23 | | #include "llvm/ADT/STLExtras.h" |
24 | | #include "llvm/ADT/SetVector.h" |
25 | | #include "llvm/ADT/SmallPtrSet.h" |
26 | | #include "llvm/ADT/SmallVector.h" |
27 | | #include "llvm/ADT/StringRef.h" |
28 | | #include "llvm/ADT/iterator.h" |
29 | | #include "llvm/Support/Chrono.h" |
30 | | #include "llvm/Support/DOTGraphTraits.h" |
31 | | #include "llvm/Support/ErrorOr.h" |
32 | | #include "llvm/Support/GraphWriter.h" |
33 | | #include "llvm/Support/MemoryBuffer.h" |
34 | | #include "llvm/Support/VirtualFileSystem.h" |
35 | | #include <algorithm> |
36 | | #include <cassert> |
37 | | #include <memory> |
38 | | #include <string> |
39 | | #include <system_error> |
40 | | |
41 | | using namespace clang; |
42 | | using namespace serialization; |
43 | | |
44 | 145 | ModuleFile *ModuleManager::lookupByFileName(StringRef Name) const { |
45 | 145 | auto Entry = FileMgr.getFile(Name, /*OpenFile=*/false, |
46 | 145 | /*CacheFailure=*/false); |
47 | 145 | if (Entry) |
48 | 145 | return lookup(*Entry); |
49 | | |
50 | 0 | return nullptr; |
51 | 145 | } |
52 | | |
53 | 55.1k | ModuleFile *ModuleManager::lookupByModuleName(StringRef Name) const { |
54 | 55.1k | if (const Module *Mod = HeaderSearchInfo.getModuleMap().findModule(Name)) |
55 | 55.1k | if (const FileEntry *File = Mod->getASTFile()) |
56 | 55.1k | return lookup(File); |
57 | | |
58 | 0 | return nullptr; |
59 | 55.1k | } |
60 | | |
61 | 55.3k | ModuleFile *ModuleManager::lookup(const FileEntry *File) const { |
62 | 55.3k | auto Known = Modules.find(File); |
63 | 55.3k | if (Known == Modules.end()) |
64 | 0 | return nullptr; |
65 | | |
66 | 55.3k | return Known->second; |
67 | 55.3k | } |
68 | | |
69 | | std::unique_ptr<llvm::MemoryBuffer> |
70 | 18.7k | ModuleManager::lookupBuffer(StringRef Name) { |
71 | 18.7k | auto Entry = FileMgr.getFile(Name, /*OpenFile=*/false, |
72 | 18.7k | /*CacheFailure=*/false); |
73 | 18.7k | if (!Entry) |
74 | 3 | return nullptr; |
75 | 18.7k | return std::move(InMemoryBuffers[*Entry]); |
76 | 18.7k | } |
77 | | |
78 | | static bool checkSignature(ASTFileSignature Signature, |
79 | | ASTFileSignature ExpectedSignature, |
80 | 23.2k | std::string &ErrorStr) { |
81 | 23.2k | if (!ExpectedSignature || Signature == ExpectedSignature22.0k ) |
82 | 23.2k | return false; |
83 | | |
84 | 6 | ErrorStr = |
85 | 6 | Signature ? "signature mismatch" : "could not read module signature"0 ; |
86 | 6 | return true; |
87 | 23.2k | } |
88 | | |
89 | | static void updateModuleImports(ModuleFile &MF, ModuleFile *ImportedBy, |
90 | 33.0k | SourceLocation ImportLoc) { |
91 | 33.0k | if (ImportedBy) { |
92 | 24.0k | MF.ImportedBy.insert(ImportedBy); |
93 | 24.0k | ImportedBy->Imports.insert(&MF); |
94 | 24.0k | } else { |
95 | 9.03k | if (!MF.DirectlyImported) |
96 | 9.03k | MF.ImportLoc = ImportLoc; |
97 | | |
98 | 9.03k | MF.DirectlyImported = true; |
99 | 9.03k | } |
100 | 33.0k | } |
101 | | |
102 | | ModuleManager::AddModuleResult |
103 | | ModuleManager::addModule(StringRef FileName, ModuleKind Type, |
104 | | SourceLocation ImportLoc, ModuleFile *ImportedBy, |
105 | | unsigned Generation, |
106 | | off_t ExpectedSize, time_t ExpectedModTime, |
107 | | ASTFileSignature ExpectedSignature, |
108 | | ASTFileSignatureReader ReadSignature, |
109 | | ModuleFile *&Module, |
110 | 34.7k | std::string &ErrorStr) { |
111 | 34.7k | Module = nullptr; |
112 | | |
113 | | // Look for the file entry. This only fails if the expected size or |
114 | | // modification time differ. |
115 | 34.7k | OptionalFileEntryRefDegradesToFileEntryPtr Entry; |
116 | 34.7k | if (Type == MK_ExplicitModule || Type == MK_PrebuiltModule32.3k ) { |
117 | | // If we're not expecting to pull this file out of the module cache, it |
118 | | // might have a different mtime due to being moved across filesystems in |
119 | | // a distributed build. The size must still match, though. (As must the |
120 | | // contents, but we can't check that.) |
121 | 2.52k | ExpectedModTime = 0; |
122 | 2.52k | } |
123 | | // Note: ExpectedSize and ExpectedModTime will be 0 for MK_ImplicitModule |
124 | | // when using an ASTFileSignature. |
125 | 34.7k | if (lookupModuleFile(FileName, ExpectedSize, ExpectedModTime, Entry)) { |
126 | 1 | ErrorStr = "module file out of date"; |
127 | 1 | return OutOfDate; |
128 | 1 | } |
129 | | |
130 | 34.7k | if (!Entry && FileName != "-"1.66k ) { |
131 | 1.66k | ErrorStr = "module file not found"; |
132 | 1.66k | return Missing; |
133 | 1.66k | } |
134 | | |
135 | | // The ModuleManager's use of FileEntry nodes as the keys for its map of |
136 | | // loaded modules is less than ideal. Uniqueness for FileEntry nodes is |
137 | | // maintained by FileManager, which in turn uses inode numbers on hosts |
138 | | // that support that. When coupled with the module cache's proclivity for |
139 | | // turning over and deleting stale PCMs, this means entries for different |
140 | | // module files can wind up reusing the same underlying inode. When this |
141 | | // happens, subsequent accesses to the Modules map will disagree on the |
142 | | // ModuleFile associated with a given file. In general, it is not sufficient |
143 | | // to resolve this conundrum with a type like FileEntryRef that stores the |
144 | | // name of the FileEntry node on first access because of path canonicalization |
145 | | // issues. However, the paths constructed for implicit module builds are |
146 | | // fully under Clang's control. We *can*, therefore, rely on their structure |
147 | | // being consistent across operating systems and across subsequent accesses |
148 | | // to the Modules map. |
149 | 33.1k | auto implicitModuleNamesMatch = [](ModuleKind Kind, const ModuleFile *MF, |
150 | 33.1k | const FileEntry *Entry) -> bool { |
151 | 14.3k | if (Kind != MK_ImplicitModule) |
152 | 897 | return true; |
153 | 13.4k | return Entry->getName() == MF->FileName; |
154 | 14.3k | }; |
155 | | |
156 | | // Check whether we already loaded this module, before |
157 | 33.1k | if (ModuleFile *ModuleEntry = Modules.lookup(Entry)) { |
158 | 14.3k | if (implicitModuleNamesMatch(Type, ModuleEntry, Entry)) { |
159 | | // Check the stored signature. |
160 | 14.3k | if (checkSignature(ModuleEntry->Signature, ExpectedSignature, ErrorStr)) |
161 | 3 | return OutOfDate; |
162 | | |
163 | 14.3k | Module = ModuleEntry; |
164 | 14.3k | updateModuleImports(*ModuleEntry, ImportedBy, ImportLoc); |
165 | 14.3k | return AlreadyLoaded; |
166 | 14.3k | } |
167 | 14.3k | } |
168 | | |
169 | | // Allocate a new module. |
170 | 18.7k | auto NewModule = std::make_unique<ModuleFile>(Type, Generation); |
171 | 18.7k | NewModule->Index = Chain.size(); |
172 | 18.7k | NewModule->FileName = FileName.str(); |
173 | 18.7k | NewModule->File = Entry; |
174 | 18.7k | NewModule->ImportLoc = ImportLoc; |
175 | 18.7k | NewModule->InputFilesValidationTimestamp = 0; |
176 | | |
177 | 18.7k | if (NewModule->Kind == MK_ImplicitModule) { |
178 | 13.0k | std::string TimestampFilename = NewModule->getTimestampFilename(); |
179 | 13.0k | llvm::vfs::Status Status; |
180 | | // A cached stat value would be fine as well. |
181 | 13.0k | if (!FileMgr.getNoncachedStatValue(TimestampFilename, Status)) |
182 | 17 | NewModule->InputFilesValidationTimestamp = |
183 | 17 | llvm::sys::toTimeT(Status.getLastModificationTime()); |
184 | 13.0k | } |
185 | | |
186 | | // Load the contents of the module |
187 | 18.7k | if (std::unique_ptr<llvm::MemoryBuffer> Buffer = lookupBuffer(FileName)) { |
188 | | // The buffer was already provided for us. |
189 | 75 | NewModule->Buffer = &ModuleCache->addBuiltPCM(FileName, std::move(Buffer)); |
190 | | // Since the cached buffer is reused, it is safe to close the file |
191 | | // descriptor that was opened while stat()ing the PCM in |
192 | | // lookupModuleFile() above, it won't be needed any longer. |
193 | 75 | Entry->closeFile(); |
194 | 18.6k | } else if (llvm::MemoryBuffer *Buffer = |
195 | 18.6k | getModuleCache().lookupPCM(FileName)) { |
196 | 4.23k | NewModule->Buffer = Buffer; |
197 | | // As above, the file descriptor is no longer needed. |
198 | 4.23k | Entry->closeFile(); |
199 | 14.4k | } else if (getModuleCache().shouldBuildPCM(FileName)) { |
200 | | // Report that the module is out of date, since we tried (and failed) to |
201 | | // import it earlier. |
202 | 19 | Entry->closeFile(); |
203 | 19 | return OutOfDate; |
204 | 14.3k | } else { |
205 | | // Open the AST file. |
206 | 14.3k | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buf((std::error_code())); |
207 | 14.3k | if (FileName == "-") { |
208 | 3 | Buf = llvm::MemoryBuffer::getSTDIN(); |
209 | 14.3k | } else { |
210 | | // Get a buffer of the file and close the file descriptor when done. |
211 | | // The file is volatile because in a parallel build we expect multiple |
212 | | // compiler processes to use the same module file rebuilding it if needed. |
213 | | // |
214 | | // RequiresNullTerminator is false because module files don't need it, and |
215 | | // this allows the file to still be mmapped. |
216 | 14.3k | Buf = FileMgr.getBufferForFile(NewModule->File, |
217 | 14.3k | /*IsVolatile=*/true, |
218 | 14.3k | /*RequiresNullTerminator=*/false); |
219 | 14.3k | } |
220 | | |
221 | 14.3k | if (!Buf) { |
222 | 0 | ErrorStr = Buf.getError().message(); |
223 | 0 | return Missing; |
224 | 0 | } |
225 | | |
226 | 14.3k | NewModule->Buffer = &getModuleCache().addPCM(FileName, std::move(*Buf)); |
227 | 14.3k | } |
228 | | |
229 | | // Initialize the stream. |
230 | 18.6k | NewModule->Data = PCHContainerRdr.ExtractPCH(*NewModule->Buffer); |
231 | | |
232 | | // Read the signature eagerly now so that we can check it. Avoid calling |
233 | | // ReadSignature unless there's something to check though. |
234 | 18.6k | if (ExpectedSignature && checkSignature(ReadSignature(NewModule->Data), |
235 | 8.87k | ExpectedSignature, ErrorStr)) |
236 | 3 | return OutOfDate; |
237 | | |
238 | | // We're keeping this module. Store it everywhere. |
239 | 18.6k | Module = Modules[Entry] = NewModule.get(); |
240 | | |
241 | 18.6k | updateModuleImports(*NewModule, ImportedBy, ImportLoc); |
242 | | |
243 | 18.6k | if (!NewModule->isModule()) |
244 | 4.09k | PCHChain.push_back(NewModule.get()); |
245 | 18.6k | if (!ImportedBy) |
246 | 8.73k | Roots.push_back(NewModule.get()); |
247 | | |
248 | 18.6k | Chain.push_back(std::move(NewModule)); |
249 | 18.6k | return NewlyLoaded; |
250 | 18.6k | } |
251 | | |
252 | 1.78k | void ModuleManager::removeModules(ModuleIterator First, ModuleMap *modMap) { |
253 | 1.78k | auto Last = end(); |
254 | 1.78k | if (First == Last) |
255 | 1.67k | return; |
256 | | |
257 | | // Explicitly clear VisitOrder since we might not notice it is stale. |
258 | 115 | VisitOrder.clear(); |
259 | | |
260 | | // Collect the set of module file pointers that we'll be removing. |
261 | 115 | llvm::SmallPtrSet<ModuleFile *, 4> victimSet( |
262 | 115 | (llvm::pointer_iterator<ModuleIterator>(First)), |
263 | 115 | (llvm::pointer_iterator<ModuleIterator>(Last))); |
264 | | |
265 | 145 | auto IsVictim = [&](ModuleFile *MF) { |
266 | 145 | return victimSet.count(MF); |
267 | 145 | }; |
268 | | // Remove any references to the now-destroyed modules. |
269 | 137 | for (auto I = begin(); I != First; ++I22 ) { |
270 | 22 | I->Imports.remove_if(IsVictim); |
271 | 22 | I->ImportedBy.remove_if(IsVictim); |
272 | 22 | } |
273 | 115 | llvm::erase_if(Roots, IsVictim); |
274 | | |
275 | | // Remove the modules from the PCH chain. |
276 | 225 | for (auto I = First; I != Last; ++I110 ) { |
277 | 142 | if (!I->isModule()) { |
278 | 32 | PCHChain.erase(llvm::find(PCHChain, &*I), PCHChain.end()); |
279 | 32 | break; |
280 | 32 | } |
281 | 142 | } |
282 | | |
283 | | // Delete the modules and erase them from the various structures. |
284 | 267 | for (ModuleIterator victim = First; victim != Last; ++victim152 ) { |
285 | 152 | Modules.erase(victim->File); |
286 | | |
287 | 152 | if (modMap) { |
288 | 131 | StringRef ModuleName = victim->ModuleName; |
289 | 131 | if (Module *mod = modMap->findModule(ModuleName)) { |
290 | 92 | mod->setASTFile(None); |
291 | 92 | } |
292 | 131 | } |
293 | 152 | } |
294 | | |
295 | | // Delete the modules. |
296 | 115 | Chain.erase(Chain.begin() + (First - begin()), Chain.end()); |
297 | 115 | } |
298 | | |
299 | | void |
300 | | ModuleManager::addInMemoryBuffer(StringRef FileName, |
301 | 75 | std::unique_ptr<llvm::MemoryBuffer> Buffer) { |
302 | 75 | const FileEntry *Entry = |
303 | 75 | FileMgr.getVirtualFile(FileName, Buffer->getBufferSize(), 0); |
304 | 75 | InMemoryBuffers[Entry] = std::move(Buffer); |
305 | 75 | } |
306 | | |
307 | 5.32M | std::unique_ptr<ModuleManager::VisitState> ModuleManager::allocateVisitState() { |
308 | | // Fast path: if we have a cached state, use it. |
309 | 5.32M | if (FirstVisitState) { |
310 | 5.31M | auto Result = std::move(FirstVisitState); |
311 | 5.31M | FirstVisitState = std::move(Result->NextState); |
312 | 5.31M | return Result; |
313 | 5.31M | } |
314 | | |
315 | | // Allocate and return a new state. |
316 | 7.87k | return std::make_unique<VisitState>(size()); |
317 | 5.32M | } |
318 | | |
319 | 5.32M | void ModuleManager::returnVisitState(std::unique_ptr<VisitState> State) { |
320 | 5.32M | assert(State->NextState == nullptr && "Visited state is in list?"); |
321 | 0 | State->NextState = std::move(FirstVisitState); |
322 | 5.32M | FirstVisitState = std::move(State); |
323 | 5.32M | } |
324 | | |
325 | 2.45k | void ModuleManager::setGlobalIndex(GlobalModuleIndex *Index) { |
326 | 2.45k | GlobalIndex = Index; |
327 | 2.45k | if (!GlobalIndex) { |
328 | 1.79k | ModulesInCommonWithGlobalIndex.clear(); |
329 | 1.79k | return; |
330 | 1.79k | } |
331 | | |
332 | | // Notify the global module index about all of the modules we've already |
333 | | // loaded. |
334 | 665 | for (ModuleFile &M : *this) |
335 | 3.61k | if (!GlobalIndex->loadedModuleFile(&M)) |
336 | 3.29k | ModulesInCommonWithGlobalIndex.push_back(&M); |
337 | 665 | } |
338 | | |
339 | 18.5k | void ModuleManager::moduleFileAccepted(ModuleFile *MF) { |
340 | 18.5k | if (!GlobalIndex || GlobalIndex->loadedModuleFile(MF)352 ) |
341 | 18.2k | return; |
342 | | |
343 | 240 | ModulesInCommonWithGlobalIndex.push_back(MF); |
344 | 240 | } |
345 | | |
346 | | ModuleManager::ModuleManager(FileManager &FileMgr, |
347 | | InMemoryModuleCache &ModuleCache, |
348 | | const PCHContainerReader &PCHContainerRdr, |
349 | | const HeaderSearch &HeaderSearchInfo) |
350 | | : FileMgr(FileMgr), ModuleCache(&ModuleCache), |
351 | 14.2k | PCHContainerRdr(PCHContainerRdr), HeaderSearchInfo(HeaderSearchInfo) {} |
352 | | |
353 | | void ModuleManager::visit(llvm::function_ref<bool(ModuleFile &M)> Visitor, |
354 | 5.32M | llvm::SmallPtrSetImpl<ModuleFile *> *ModuleFilesHit) { |
355 | | // If the visitation order vector is the wrong size, recompute the order. |
356 | 5.32M | if (VisitOrder.size() != Chain.size()) { |
357 | 7.46k | unsigned N = size(); |
358 | 7.46k | VisitOrder.clear(); |
359 | 7.46k | VisitOrder.reserve(N); |
360 | | |
361 | | // Record the number of incoming edges for each module. When we |
362 | | // encounter a module with no incoming edges, push it into the queue |
363 | | // to seed the queue. |
364 | 7.46k | SmallVector<ModuleFile *, 4> Queue; |
365 | 7.46k | Queue.reserve(N); |
366 | 7.46k | llvm::SmallVector<unsigned, 4> UnusedIncomingEdges; |
367 | 7.46k | UnusedIncomingEdges.resize(size()); |
368 | 20.4k | for (ModuleFile &M : llvm::reverse(*this)) { |
369 | 20.4k | unsigned Size = M.ImportedBy.size(); |
370 | 20.4k | UnusedIncomingEdges[M.Index] = Size; |
371 | 20.4k | if (!Size) |
372 | 8.56k | Queue.push_back(&M); |
373 | 20.4k | } |
374 | | |
375 | | // Traverse the graph, making sure to visit a module before visiting any |
376 | | // of its dependencies. |
377 | 27.9k | while (!Queue.empty()) { |
378 | 20.4k | ModuleFile *CurrentModule = Queue.pop_back_val(); |
379 | 20.4k | VisitOrder.push_back(CurrentModule); |
380 | | |
381 | | // For any module that this module depends on, push it on the |
382 | | // stack (if it hasn't already been marked as visited). |
383 | 29.3k | for (ModuleFile *M : llvm::reverse(CurrentModule->Imports)) { |
384 | | // Remove our current module as an impediment to visiting the |
385 | | // module we depend on. If we were the last unvisited module |
386 | | // that depends on this particular module, push it into the |
387 | | // queue to be visited. |
388 | 29.3k | unsigned &NumUnusedEdges = UnusedIncomingEdges[M->Index]; |
389 | 29.3k | if (NumUnusedEdges && (--NumUnusedEdges == 0)) |
390 | 11.8k | Queue.push_back(M); |
391 | 29.3k | } |
392 | 20.4k | } |
393 | | |
394 | 7.46k | assert(VisitOrder.size() == N && "Visitation order is wrong?"); |
395 | | |
396 | 0 | FirstVisitState = nullptr; |
397 | 7.46k | } |
398 | | |
399 | 0 | auto State = allocateVisitState(); |
400 | 5.32M | unsigned VisitNumber = State->NextVisitNumber++; |
401 | | |
402 | | // If the caller has provided us with a hit-set that came from the global |
403 | | // module index, mark every module file in common with the global module |
404 | | // index that is *not* in that set as 'visited'. |
405 | 5.32M | if (ModuleFilesHit && !ModulesInCommonWithGlobalIndex.empty()100k ) { |
406 | 1.71M | for (unsigned I = 0, N = ModulesInCommonWithGlobalIndex.size(); I != N; ++I1.61M ) |
407 | 1.61M | { |
408 | 1.61M | ModuleFile *M = ModulesInCommonWithGlobalIndex[I]; |
409 | 1.61M | if (!ModuleFilesHit->count(M)) |
410 | 1.40M | State->VisitNumber[M->Index] = VisitNumber; |
411 | 1.61M | } |
412 | 99.3k | } |
413 | | |
414 | 51.3M | for (unsigned I = 0, N = VisitOrder.size(); I != N; ++I46.0M ) { |
415 | 46.0M | ModuleFile *CurrentModule = VisitOrder[I]; |
416 | | // Should we skip this module file? |
417 | 46.0M | if (State->VisitNumber[CurrentModule->Index] == VisitNumber) |
418 | 9.17M | continue; |
419 | | |
420 | | // Visit the module. |
421 | 36.8M | assert(State->VisitNumber[CurrentModule->Index] == VisitNumber - 1); |
422 | 0 | State->VisitNumber[CurrentModule->Index] = VisitNumber; |
423 | 36.8M | if (!Visitor(*CurrentModule)) |
424 | 34.4M | continue; |
425 | | |
426 | | // The visitor has requested that cut off visitation of any |
427 | | // module that the current module depends on. To indicate this |
428 | | // behavior, we mark all of the reachable modules as having been visited. |
429 | 2.46M | ModuleFile *NextModule = CurrentModule; |
430 | 10.2M | do { |
431 | | // For any module that this module depends on, push it on the |
432 | | // stack (if it hasn't already been marked as visited). |
433 | 10.2M | for (llvm::SetVector<ModuleFile *>::iterator |
434 | 10.2M | M = NextModule->Imports.begin(), |
435 | 10.2M | MEnd = NextModule->Imports.end(); |
436 | 27.2M | M != MEnd; ++M17.0M ) { |
437 | 17.0M | if (State->VisitNumber[(*M)->Index] != VisitNumber) { |
438 | 7.77M | State->Stack.push_back(*M); |
439 | 7.77M | State->VisitNumber[(*M)->Index] = VisitNumber; |
440 | 7.77M | } |
441 | 17.0M | } |
442 | | |
443 | 10.2M | if (State->Stack.empty()) |
444 | 2.46M | break; |
445 | | |
446 | | // Pop the next module off the stack. |
447 | 7.77M | NextModule = State->Stack.pop_back_val(); |
448 | 7.77M | } while (true); |
449 | 2.46M | } |
450 | | |
451 | 5.32M | returnVisitState(std::move(State)); |
452 | 5.32M | } |
453 | | |
454 | | bool ModuleManager::lookupModuleFile(StringRef FileName, off_t ExpectedSize, |
455 | | time_t ExpectedModTime, |
456 | 34.7k | Optional<FileEntryRef> &File) { |
457 | 34.7k | File = None; |
458 | 34.7k | if (FileName == "-") |
459 | 3 | return false; |
460 | | |
461 | | // Open the file immediately to ensure there is no race between stat'ing and |
462 | | // opening the file. |
463 | 34.7k | Optional<FileEntryRef> FileOrErr = |
464 | 34.7k | expectedToOptional(FileMgr.getFileRef(FileName, /*OpenFile=*/true, |
465 | 34.7k | /*CacheFailure=*/false)); |
466 | 34.7k | if (!FileOrErr) |
467 | 1.66k | return false; |
468 | | |
469 | 33.1k | File = *FileOrErr; |
470 | | |
471 | 33.1k | if ((ExpectedSize && ExpectedSize != File->getSize()1.96k ) || |
472 | 33.1k | (ExpectedModTime && ExpectedModTime != File->getModificationTime()122 )) |
473 | | // Do not destroy File, as it may be referenced. If we need to rebuild it, |
474 | | // it will be destroyed by removeModules. |
475 | 1 | return true; |
476 | | |
477 | 33.1k | return false; |
478 | 33.1k | } |
479 | | |
480 | | #ifndef NDEBUG |
481 | | namespace llvm { |
482 | | |
483 | | template<> |
484 | | struct GraphTraits<ModuleManager> { |
485 | | using NodeRef = ModuleFile *; |
486 | | using ChildIteratorType = llvm::SetVector<ModuleFile *>::const_iterator; |
487 | | using nodes_iterator = pointer_iterator<ModuleManager::ModuleConstIterator>; |
488 | | |
489 | 0 | static ChildIteratorType child_begin(NodeRef Node) { |
490 | 0 | return Node->Imports.begin(); |
491 | 0 | } |
492 | | |
493 | 0 | static ChildIteratorType child_end(NodeRef Node) { |
494 | 0 | return Node->Imports.end(); |
495 | 0 | } |
496 | | |
497 | 0 | static nodes_iterator nodes_begin(const ModuleManager &Manager) { |
498 | 0 | return nodes_iterator(Manager.begin()); |
499 | 0 | } |
500 | | |
501 | 0 | static nodes_iterator nodes_end(const ModuleManager &Manager) { |
502 | 0 | return nodes_iterator(Manager.end()); |
503 | 0 | } |
504 | | }; |
505 | | |
506 | | template<> |
507 | | struct DOTGraphTraits<ModuleManager> : public DefaultDOTGraphTraits { |
508 | | explicit DOTGraphTraits(bool IsSimple = false) |
509 | 0 | : DefaultDOTGraphTraits(IsSimple) {} |
510 | | |
511 | 0 | static bool renderGraphFromBottomUp() { return true; } |
512 | | |
513 | 0 | std::string getNodeLabel(ModuleFile *M, const ModuleManager&) { |
514 | 0 | return M->ModuleName; |
515 | 0 | } |
516 | | }; |
517 | | |
518 | | } // namespace llvm |
519 | | |
520 | 0 | void ModuleManager::viewGraph() { |
521 | 0 | llvm::ViewGraph(*this, "Modules"); |
522 | 0 | } |
523 | | #endif |