1//===-CachePruning.cpp - LLVM Cache Directory Pruning ---------------------===// 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 7//===----------------------------------------------------------------------===// 9// This file implements the pruning of a directory based on least recently used. 11//===----------------------------------------------------------------------===// 23#define DEBUG_TYPE "cache-pruning" 26#include <system_error> 36 /// Used to determine which files to prune first. Also used to determine 37 /// set membership, so must take into account all fields. 39return std::tie(Time,
Other.Size, Path) <
43}
// anonymous namespace 45/// Write a new timestamp file with the given path. This is used for the pruning 54return make_error<StringError>(
"Duration must not be empty",
60return make_error<StringError>(
"'" + NumStr +
"' not an integer",
65return std::chrono::seconds(Num);
67return std::chrono::minutes(Num);
69return std::chrono::hours(Num);
71return make_error<StringError>(
"'" +
Duration +
72"' must end with one of 's', 'm' or 'h'",
80 std::pair<StringRef, StringRef>
P = {
"", PolicyStr};
81while (!
P.second.empty()) {
82P =
P.second.split(
':');
85 std::tie(Key,
Value) =
P.first.split(
'=');
86if (Key ==
"prune_interval") {
89return DurationOrErr.takeError();
91 }
elseif (Key ==
"prune_after") {
94return DurationOrErr.takeError();
96 }
elseif (Key ==
"cache_size") {
97if (
Value.back() !=
'%')
98return make_error<StringError>(
"'" +
Value +
"' must be a percentage",
103return make_error<StringError>(
"'" + SizeStr +
"' not an integer",
106return make_error<StringError>(
"'" + SizeStr +
107"' must be between 0 and 100",
110 }
elseif (Key ==
"cache_size_bytes") {
112switch (tolower(
Value.back())) {
122 Mult = 1024 * 1024 * 1024;
128return make_error<StringError>(
"'" +
Value +
"' not an integer",
131 }
elseif (Key ==
"cache_size_files") {
133return make_error<StringError>(
"'" +
Value +
"' not an integer",
136return make_error<StringError>(
"Unknown key: '" + Key +
"'",
144/// Prune the cache of files that haven't been accessed in a long time. 146const std::vector<std::unique_ptr<MemoryBuffer>> &Files) {
147using namespacestd::chrono;
166// Nothing will be pruned, early exit 170// Try to stat() the timestamp file. 174constauto CurrentTime = system_clock::now();
176if (EC == errc::no_such_file_or_directory) {
177// If the timestamp file wasn't there, create one now. 187// Check whether the time stamp is older than our pruning interval. 188// If not, do nothing. 190auto TimeStampAge = CurrentTime - TimeStampModTime;
191if (TimeStampAge <= *Policy.
Interval) {
193 << duration_cast<seconds>(TimeStampAge).
count()
194 <<
"s old), do not prune.\n");
198// Write a new timestamp file so that nobody else attempts to prune. 199// There is a benign race condition here, if two processes happen to 200// notice at the same time that the timestamp is out-of-date. 204// Keep track of files to delete to get below the size limit. 205// Order by time of last use so that recently used files are preserved. 206 std::set<FileInfo> FileInfos;
209// Walk the entire directory cache, looking for unused files. 213// Walk all of the files within this directory. 215 File != FileEnd && !EC; File.increment(EC)) {
216// Ignore filenames not beginning with "llvmcache-" or "Thin-". This 217// includes the timestamp file as well as any files created by the user. 218// This acts as a safeguard against data loss if the user specifies the 219// wrong directory as their cache directory. 224// Look at this file. If we can't stat it, there's nothing interesting 228LLVM_DEBUG(
dbgs() <<
"Ignore " << File->path() <<
" (can't stat)\n");
232// If the file hasn't been used recently enough, delete it 233constauto FileAccessTime = StatusOrErr->getLastAccessedTime();
234auto FileAge = CurrentTime - FileAccessTime;
237 << duration_cast<seconds>(FileAge).count()
243// Leave it here for now, but add it to the list of size-based pruning. 244 TotalSize += StatusOrErr->getSize();
245 FileInfos.insert({FileAccessTime, StatusOrErr->getSize(), File->path()});
248auto FileInfo = FileInfos.begin();
249size_t NumFiles = FileInfos.size();
251auto RemoveCacheFile = [&]() {
255 TotalSize -= FileInfo->Size;
258 << FileInfo->Size <<
"), new occupancy is " << TotalSize
263// files.size() is greater the number of inputs by one. However, a timestamp 264// file is created and stored in the cache directory if --thinlto-cache-policy 265// option is used. Therefore, files.size() is used as ActualNums. 266constsize_t ActualNums = Files.size();
269 <<
"ThinLTO cache pruning happens since the number of created files (" 270 << ActualNums <<
") exceeds the maximum number of files (" 272 <<
"); consider adjusting --thinlto-cache-policy\n";
274// Prune for number of files. 279// Prune for size now if needed 282if (!ErrOrSpaceInfo) {
286auto AvailableSpace = TotalSize + SpaceInfo.
free;
292auto TotalSizeTarget = std::min<uint64_t>(
296LLVM_DEBUG(
dbgs() <<
"Occupancy: " << ((100 * TotalSize) / AvailableSpace)
301size_t ActualSizes = 0;
302for (
constauto &File : Files)
304 ActualSizes += File->getBufferSize();
306if (ActualSizes > TotalSizeTarget)
308 <<
"ThinLTO cache pruning happens since the total size of the cache " 309"files consumed by the current link job (" 310 << ActualSizes <<
" bytes) exceeds maximum cache size (" 312 <<
" bytes); consider adjusting --thinlto-cache-policy\n";
314// Remove the oldest accessed files first, till we get below the threshold. 315while (TotalSize > TotalSizeTarget && FileInfo != FileInfos.end())
static Expected< std::chrono::seconds > parseDuration(StringRef Duration)
static void writeTimestampFile(StringRef TimestampFile)
Write a new timestamp file with the given path.
std::optional< std::vector< StOtherPiece > > Other
Represents either an error or a value T.
Tagged union holding either a T or a Error.
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
StringRef - Represent a constant reference to a string, i.e.
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
std::string str() const
str - Get the contents as an std::string.
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
LLVM Value Representation.
static raw_ostream & warning()
Convenience method for printing "warning: " to stderr.
A raw_ostream that writes to a file descriptor.
TimePoint getLastModificationTime() const
The file modification time as reported from the underlying file system.
directory_iterator - Iterates through the entries in path.
Represents the result of a call to sys::fs::status().
std::error_code status(const Twine &path, file_status &result, bool follow=true)
Get file status as if by POSIX stat().
std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
ErrorOr< space_info > disk_space(const Twine &Path)
Get disk space usage information.
bool is_directory(const basic_file_status &status)
Does status represent a directory?
StringRef filename(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get filename.
void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
std::chrono::time_point< std::chrono::system_clock, D > TimePoint
A time point on the system clock.
This is an optimization pass for GlobalISel generic memory operations.
bool operator<(int64_t V1, const APSInt &V2)
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Expected< CachePruningPolicy > parseCachePruningPolicy(StringRef PolicyStr)
Parse the given string as a cache pruning policy.
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
bool pruneCache(StringRef Path, CachePruningPolicy Policy, const std::vector< std::unique_ptr< MemoryBuffer > > &Files={})
Peform pruning using the supplied policy, returns true if pruning occurred, i.e.
auto count(R &&Range, const E &Element)
Wrapper function around std::count to count the number of times an element Element occurs in the give...
Policy for the pruneCache() function.
uint64_t MaxSizeFiles
The maximum number of files in the cache directory.
std::optional< std::chrono::seconds > Interval
The pruning interval.
std::chrono::seconds Expiration
The expiration for a file.
uint64_t MaxSizeBytes
The maximum size for the cache directory in bytes.
unsigned MaxSizePercentageOfAvailableSpace
The maximum size for the cache directory, in terms of percentage of the available space on the disk.
space_info - Self explanatory.