CachePruning.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. //===-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. #include "llvm/Support/CachePruning.h"
  14. #include "llvm/Support/Debug.h"
  15. #include "llvm/Support/Errc.h"
  16. #include "llvm/Support/FileSystem.h"
  17. #include "llvm/Support/Path.h"
  18. #include "llvm/Support/raw_ostream.h"
  19. #define DEBUG_TYPE "cache-pruning"
  20. #include <set>
  21. #include <system_error>
  22. using namespace llvm;
  23. /// Write a new timestamp file with the given path. This is used for the pruning
  24. /// interval option.
  25. static void writeTimestampFile(StringRef TimestampFile) {
  26. std::error_code EC;
  27. raw_fd_ostream Out(TimestampFile.str(), EC, sys::fs::F_None);
  28. }
  29. /// Prune the cache of files that haven't been accessed in a long time.
  30. bool llvm::pruneCache(StringRef Path, CachePruningPolicy Policy) {
  31. using namespace std::chrono;
  32. if (Path.empty())
  33. return false;
  34. bool isPathDir;
  35. if (sys::fs::is_directory(Path, isPathDir))
  36. return false;
  37. if (!isPathDir)
  38. return false;
  39. Policy.PercentageOfAvailableSpace =
  40. std::min(Policy.PercentageOfAvailableSpace, 100u);
  41. if (Policy.Expiration == seconds(0) &&
  42. Policy.PercentageOfAvailableSpace == 0) {
  43. DEBUG(dbgs() << "No pruning settings set, exit early\n");
  44. // Nothing will be pruned, early exit
  45. return false;
  46. }
  47. // Try to stat() the timestamp file.
  48. SmallString<128> TimestampFile(Path);
  49. sys::path::append(TimestampFile, "llvmcache.timestamp");
  50. sys::fs::file_status FileStatus;
  51. const auto CurrentTime = system_clock::now();
  52. if (auto EC = sys::fs::status(TimestampFile, FileStatus)) {
  53. if (EC == errc::no_such_file_or_directory) {
  54. // If the timestamp file wasn't there, create one now.
  55. writeTimestampFile(TimestampFile);
  56. } else {
  57. // Unknown error?
  58. return false;
  59. }
  60. } else {
  61. if (Policy.Interval == seconds(0)) {
  62. // Check whether the time stamp is older than our pruning interval.
  63. // If not, do nothing.
  64. const auto TimeStampModTime = FileStatus.getLastModificationTime();
  65. auto TimeStampAge = CurrentTime - TimeStampModTime;
  66. if (TimeStampAge <= Policy.Interval) {
  67. DEBUG(dbgs() << "Timestamp file too recent ("
  68. << duration_cast<seconds>(TimeStampAge).count()
  69. << "s old), do not prune.\n");
  70. return false;
  71. }
  72. }
  73. // Write a new timestamp file so that nobody else attempts to prune.
  74. // There is a benign race condition here, if two processes happen to
  75. // notice at the same time that the timestamp is out-of-date.
  76. writeTimestampFile(TimestampFile);
  77. }
  78. bool ShouldComputeSize = (Policy.PercentageOfAvailableSpace > 0);
  79. // Keep track of space
  80. std::set<std::pair<uint64_t, std::string>> FileSizes;
  81. uint64_t TotalSize = 0;
  82. // Helper to add a path to the set of files to consider for size-based
  83. // pruning, sorted by size.
  84. auto AddToFileListForSizePruning =
  85. [&](StringRef Path) {
  86. if (!ShouldComputeSize)
  87. return;
  88. TotalSize += FileStatus.getSize();
  89. FileSizes.insert(
  90. std::make_pair(FileStatus.getSize(), std::string(Path)));
  91. };
  92. // Walk the entire directory cache, looking for unused files.
  93. std::error_code EC;
  94. SmallString<128> CachePathNative;
  95. sys::path::native(Path, CachePathNative);
  96. // Walk all of the files within this directory.
  97. for (sys::fs::directory_iterator File(CachePathNative, EC), FileEnd;
  98. File != FileEnd && !EC; File.increment(EC)) {
  99. // Do not touch the timestamp.
  100. if (File->path() == TimestampFile)
  101. continue;
  102. // Look at this file. If we can't stat it, there's nothing interesting
  103. // there.
  104. if (sys::fs::status(File->path(), FileStatus)) {
  105. DEBUG(dbgs() << "Ignore " << File->path() << " (can't stat)\n");
  106. continue;
  107. }
  108. // If the file hasn't been used recently enough, delete it
  109. const auto FileAccessTime = FileStatus.getLastAccessedTime();
  110. auto FileAge = CurrentTime - FileAccessTime;
  111. if (FileAge > Policy.Expiration) {
  112. DEBUG(dbgs() << "Remove " << File->path() << " ("
  113. << duration_cast<seconds>(FileAge).count() << "s old)\n");
  114. sys::fs::remove(File->path());
  115. continue;
  116. }
  117. // Leave it here for now, but add it to the list of size-based pruning.
  118. AddToFileListForSizePruning(File->path());
  119. }
  120. // Prune for size now if needed
  121. if (ShouldComputeSize) {
  122. auto ErrOrSpaceInfo = sys::fs::disk_space(Path);
  123. if (!ErrOrSpaceInfo) {
  124. report_fatal_error("Can't get available size");
  125. }
  126. sys::fs::space_info SpaceInfo = ErrOrSpaceInfo.get();
  127. auto AvailableSpace = TotalSize + SpaceInfo.free;
  128. auto FileAndSize = FileSizes.rbegin();
  129. DEBUG(dbgs() << "Occupancy: " << ((100 * TotalSize) / AvailableSpace)
  130. << "% target is: " << Policy.PercentageOfAvailableSpace
  131. << "\n");
  132. // Remove the oldest accessed files first, till we get below the threshold
  133. while (((100 * TotalSize) / AvailableSpace) >
  134. Policy.PercentageOfAvailableSpace &&
  135. FileAndSize != FileSizes.rend()) {
  136. // Remove the file.
  137. sys::fs::remove(FileAndSize->second);
  138. // Update size
  139. TotalSize -= FileAndSize->first;
  140. DEBUG(dbgs() << " - Remove " << FileAndSize->second << " (size "
  141. << FileAndSize->first << "), new occupancy is " << TotalSize
  142. << "%\n");
  143. ++FileAndSize;
  144. }
  145. }
  146. return true;
  147. }