CachePruning.cpp 4.5 KB

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