blob: 0a3aef807e4409dd8322e1df75c4e2e737003758 [file] [log] [blame]
Mehdi Amini27814982016-04-02 03:28:26 +00001//===-CachePruning.cpp - LLVM Cache Directory Pruning ---------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements the pruning of a directory based on least recently used.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Support/CachePruning.h"
15
16#include "llvm/Support/FileSystem.h"
17#include "llvm/Support/Path.h"
18#include "llvm/Support/raw_ostream.h"
19
20#include <set>
21
22using namespace llvm;
23
24/// Write a new timestamp file with the given path. This is used for the pruning
25/// interval option.
26static void writeTimestampFile(StringRef TimestampFile) {
27 std::error_code EC;
28 raw_fd_ostream Out(TimestampFile.str(), EC, sys::fs::F_None);
29}
30
31/// Prune the cache of files that haven't been accessed in a long time.
32bool CachePruning::prune() {
33 SmallString<128> TimestampFile(Path);
34 sys::path::append(TimestampFile, "llvmcache.timestamp");
35
36 if (Expiration == 0 && PercentageOfAvailableSpace == 0)
37 // Nothing will be pruned, early exit
38 return false;
39
40 // Try to stat() the timestamp file.
41 sys::fs::file_status FileStatus;
42 sys::TimeValue CurrentTime = sys::TimeValue::now();
43 if (sys::fs::status(TimestampFile, FileStatus)) {
44 if (errno == ENOENT) {
45 // If the timestamp file wasn't there, create one now.
46 writeTimestampFile(TimestampFile);
47 } else {
48 // Unknown error?
49 return false;
50 }
51 } else {
52 if (Interval) {
53 // Check whether the time stamp is older than our pruning interval.
54 // If not, do nothing.
55 sys::TimeValue TimeStampModTime = FileStatus.getLastModificationTime();
56 auto TimeInterval = sys::TimeValue(sys::TimeValue::SecondsType(Interval));
57 if (CurrentTime - TimeStampModTime <= TimeInterval)
58 return false;
59 }
60 // Write a new timestamp file so that nobody else attempts to prune.
61 // There is a benign race condition here, if two processes happen to
62 // notice at the same time that the timestamp is out-of-date.
63 writeTimestampFile(TimestampFile);
64 }
65
66 bool ShouldComputeSize = (PercentageOfAvailableSpace > 0);
67
68 // Keep track of space
69 std::set<std::pair<uint64_t, std::string>> FileSizes;
70 uint64_t TotalSize = 0;
71 // Helper to add a path to the set of files to consider for size-based
72 // pruning, sorted by last accessed time.
73 auto AddToFileListForSizePruning =
74 [&](StringRef Path, sys::TimeValue FileAccessTime) {
75 if (!ShouldComputeSize)
76 return;
77 TotalSize += FileStatus.getSize();
78 FileSizes.insert(
79 std::make_pair(FileAccessTime.seconds(), std::string(Path)));
80 };
81
82 // Walk the entire directory cache, looking for unused files.
83 std::error_code EC;
84 SmallString<128> CachePathNative;
85 sys::path::native(Path, CachePathNative);
86 auto TimeExpiration = sys::TimeValue(sys::TimeValue::SecondsType(Expiration));
87 // Walk all of the files within this directory.
88 for (sys::fs::directory_iterator File(CachePathNative, EC), FileEnd;
89 File != FileEnd && !EC; File.increment(EC)) {
90 // Do not touch the timestamp.
91 if (File->path() == TimestampFile)
92 continue;
93
94 // Look at this file. If we can't stat it, there's nothing interesting
95 // there.
96 if (sys::fs::status(File->path(), FileStatus))
97 continue;
98
99 // If the file hasn't been used recently enough, delete it
100 sys::TimeValue FileAccessTime = FileStatus.getLastAccessedTime();
101 if (CurrentTime - FileAccessTime > TimeExpiration) {
102 sys::fs::remove(File->path());
103 continue;
104 }
105
106 // Leave it here for now, but add it to the list of size-based pruning.
107 AddToFileListForSizePruning(File->path(), FileAccessTime);
108 }
109
110 // Prune for size now if needed
111 if (ShouldComputeSize) {
112 auto ErrOrSpaceInfo = sys::fs::disk_space(Path);
113 if (!ErrOrSpaceInfo) {
114 report_fatal_error("Can't get available size");
115 }
116 sys::fs::space_info SpaceInfo = ErrOrSpaceInfo.get();
117 auto AvailableSpace = TotalSize + SpaceInfo.free;
118 auto FileAndSize = FileSizes.rbegin();
119 // Remove the oldest accessed files first, till we get below the threshold
120 while (((100 * TotalSize) / AvailableSpace) > PercentageOfAvailableSpace &&
121 FileAndSize != FileSizes.rend()) {
122 // Remove the file.
123 sys::fs::remove(FileAndSize->second);
124 // Update size
125 TotalSize -= FileAndSize->first;
126 ++FileAndSize;
127 }
128 }
129 return true;
130}