clang 22.0.0git
ModuleCache.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
10
13#include "llvm/Support/FileSystem.h"
14#include "llvm/Support/IOSandbox.h"
15#include "llvm/Support/LockFileManager.h"
16#include "llvm/Support/Path.h"
17
18using namespace clang;
19
20/// Write a new timestamp file with the given path.
21static void writeTimestampFile(StringRef TimestampFile) {
22 std::error_code EC;
23 llvm::raw_fd_ostream Out(TimestampFile.str(), EC, llvm::sys::fs::OF_None);
24}
25
26void clang::maybePruneImpl(StringRef Path, time_t PruneInterval,
27 time_t PruneAfter) {
28 if (PruneInterval <= 0 || PruneAfter <= 0)
29 return;
30
31 // This is a compiler-internal input/output, let's bypass the sandbox.
32 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
33
34 llvm::SmallString<128> TimestampFile(Path);
35 llvm::sys::path::append(TimestampFile, "modules.timestamp");
36
37 // Try to stat() the timestamp file.
38 llvm::sys::fs::file_status StatBuf;
39 if (std::error_code EC = llvm::sys::fs::status(TimestampFile, StatBuf)) {
40 // If the timestamp file wasn't there, create one now.
41 if (EC == std::errc::no_such_file_or_directory)
42 writeTimestampFile(TimestampFile);
43 return;
44 }
45
46 // Check whether the time stamp is older than our pruning interval.
47 // If not, do nothing.
48 time_t TimestampModTime =
49 llvm::sys::toTimeT(StatBuf.getLastModificationTime());
50 time_t CurrentTime = time(nullptr);
51 if (CurrentTime - TimestampModTime <= PruneInterval)
52 return;
53
54 // Write a new timestamp file so that nobody else attempts to prune.
55 // There is a benign race condition here, if two Clang instances happen to
56 // notice at the same time that the timestamp is out-of-date.
57 writeTimestampFile(TimestampFile);
58
59 // Walk the entire module cache, looking for unused module files and module
60 // indices.
61 std::error_code EC;
62 for (llvm::sys::fs::directory_iterator Dir(Path, EC), DirEnd;
63 Dir != DirEnd && !EC; Dir.increment(EC)) {
64 // If we don't have a directory, there's nothing to look into.
65 if (!llvm::sys::fs::is_directory(Dir->path()))
66 continue;
67
68 // Walk all the files within this directory.
69 for (llvm::sys::fs::directory_iterator File(Dir->path(), EC), FileEnd;
70 File != FileEnd && !EC; File.increment(EC)) {
71 // We only care about module and global module index files.
72 StringRef Extension = llvm::sys::path::extension(File->path());
73 if (Extension != ".pcm" && Extension != ".timestamp" &&
74 llvm::sys::path::filename(File->path()) != "modules.idx")
75 continue;
76
77 // Look at this file. If we can't stat it, there's nothing interesting
78 // there.
79 if (llvm::sys::fs::status(File->path(), StatBuf))
80 continue;
81
82 // If the file has been used recently enough, leave it there.
83 time_t FileAccessTime = llvm::sys::toTimeT(StatBuf.getLastAccessedTime());
84 if (CurrentTime - FileAccessTime <= PruneAfter)
85 continue;
86
87 // Remove the file.
88 llvm::sys::fs::remove(File->path());
89
90 // Remove the timestamp file.
91 std::string TimpestampFilename = File->path() + ".timestamp";
92 llvm::sys::fs::remove(TimpestampFilename);
93 }
94
95 // If we removed all the files in the directory, remove the directory
96 // itself.
97 if (llvm::sys::fs::directory_iterator(Dir->path(), EC) ==
98 llvm::sys::fs::directory_iterator() &&
99 !EC)
100 llvm::sys::fs::remove(Dir->path());
101 }
102}
103
104namespace {
105class CrossProcessModuleCache : public ModuleCache {
106 InMemoryModuleCache InMemory;
107
108public:
109 void prepareForGetLock(StringRef ModuleFilename) override {
110 // This is a compiler-internal input/output, let's bypass the sandbox.
111 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
112
113 // FIXME: Do this in LockFileManager and only if the directory doesn't
114 // exist.
115 StringRef Dir = llvm::sys::path::parent_path(ModuleFilename);
116 llvm::sys::fs::create_directories(Dir);
117 }
118
119 std::unique_ptr<llvm::AdvisoryLock>
120 getLock(StringRef ModuleFilename) override {
121 return std::make_unique<llvm::LockFileManager>(ModuleFilename);
122 }
123
124 std::time_t getModuleTimestamp(StringRef ModuleFilename) override {
125 // This is a compiler-internal input/output, let's bypass the sandbox.
126 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
127
128 std::string TimestampFilename =
130 llvm::sys::fs::file_status Status;
131 if (llvm::sys::fs::status(TimestampFilename, Status) != std::error_code{})
132 return 0;
133 return llvm::sys::toTimeT(Status.getLastModificationTime());
134 }
135
136 void updateModuleTimestamp(StringRef ModuleFilename) override {
137 // This is a compiler-internal input/output, let's bypass the sandbox.
138 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
139
140 // Overwrite the timestamp file contents so that file's mtime changes.
141 std::error_code EC;
142 llvm::raw_fd_ostream OS(
144 llvm::sys::fs::OF_TextWithCRLF);
145 if (EC)
146 return;
147 OS << "Timestamp file\n";
148 OS.close();
149 OS.clear_error(); // Avoid triggering a fatal error.
150 }
151
152 void maybePrune(StringRef Path, time_t PruneInterval,
153 time_t PruneAfter) override {
154 // This is a compiler-internal input/output, let's bypass the sandbox.
155 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
156
157 maybePruneImpl(Path, PruneInterval, PruneAfter);
158 }
159
160 InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
161 const InMemoryModuleCache &getInMemoryModuleCache() const override {
162 return InMemory;
163 }
164};
165} // namespace
166
168 return llvm::makeIntrusiveRefCnt<CrossProcessModuleCache>();
169}
static void writeTimestampFile(StringRef TimestampFile)
Write a new timestamp file with the given path.
In-memory cache for modules.
The module cache used for compiling modules implicitly.
Definition ModuleCache.h:26
static std::string getTimestampFilename(StringRef FileName)
Definition ModuleFile.h:153
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
The JSON file list parser is used to communicate input to InstallAPI.
IntrusiveRefCntPtr< ModuleCache > createCrossProcessModuleCache()
Creates new ModuleCache backed by a file system directory that may be operated on by multiple process...
void maybePruneImpl(StringRef Path, time_t PruneInterval, time_t PruneAfter)
Shared implementation of ModuleCache::maybePrune().