OSDN Git Service

Support: Add a cache pruning policy parser.
authorPeter Collingbourne <peter@pcc.me.uk>
Thu, 16 Mar 2017 03:42:00 +0000 (03:42 +0000)
committerPeter Collingbourne <peter@pcc.me.uk>
Thu, 16 Mar 2017 03:42:00 +0000 (03:42 +0000)
The idea is that the policy string fully specifies the policy and is portable
between clients.

Differential Revision: https://reviews.llvm.org/D31020

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@297927 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/LTO/legacy/ThinLTOCodeGenerator.h
include/llvm/Support/CachePruning.h
lib/Support/CachePruning.cpp
unittests/Support/CMakeLists.txt
unittests/Support/CachePruningTest.cpp [new file with mode: 0644]

index 8fdfd3d..28e61cb 100644 (file)
@@ -142,12 +142,6 @@ public:
   struct CachingOptions {
     std::string Path;                    // Path to the cache, empty to disable.
     CachePruningPolicy Policy;
-
-    CachingOptions() {
-      Policy.Interval = std::chrono::seconds(1200);
-      Policy.Expiration = std::chrono::hours(7 * 24); // 1w
-      Policy.PercentageOfAvailableSpace = 75;
-    };
   };
 
   /// Provide a path to a directory where to store the cached files for
index 282ecd0..46bab38 100644 (file)
 
 namespace llvm {
 
+template <typename T> class Expected;
+
+/// Policy for the pruneCache() function. A default constructed
+/// CachePruningPolicy provides a reasonable default policy.
 struct CachePruningPolicy {
   /// The pruning interval. This is intended to be used to avoid scanning the
   /// directory too often. It does not impact the decision of which file to
   /// prune. A value of 0 forces the scan to occur.
-  std::chrono::seconds Interval = std::chrono::seconds::zero();
+  std::chrono::seconds Interval = std::chrono::seconds(1200);
 
   /// The expiration for a file. When a file hasn't been accessed for Expiration
   /// seconds, it is removed from the cache. A value of 0 disables the
   /// expiration-based pruning.
-  std::chrono::seconds Expiration = std::chrono::seconds::zero();
+  std::chrono::seconds Expiration = std::chrono::hours(7 * 24); // 1w
 
   /// The maximum size for the cache directory, in terms of percentage of the
   /// available space on the the disk. Set to 100 to indicate no limit, 50 to
   /// indicate that the cache size will not be left over half the available disk
   /// space. A value over 100 will be reduced to 100. A value of 0 disables the
   /// size-based pruning.
-  unsigned PercentageOfAvailableSpace = 0;
+  unsigned PercentageOfAvailableSpace = 75;
 };
 
+/// Parse the given string as a cache pruning policy. Defaults are taken from a
+/// default constructed CachePruningPolicy object.
+/// For example: "prune_interval=30s:prune_after=24h:cache_size=50%"
+/// which means a pruning interval of 30 seconds, expiration time of 24 hours
+/// and maximum cache size of 50% of available disk space.
+Expected<CachePruningPolicy> parseCachePruningPolicy(StringRef PolicyStr);
+
 /// Peform pruning using the supplied policy, returns true if pruning
 /// occured, i.e. if Policy.Interval was expired.
 bool pruneCache(StringRef Path, CachePruningPolicy Policy);
index 27cffb7..73e55f2 100644 (file)
@@ -15,6 +15,7 @@
 
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
@@ -33,6 +34,73 @@ static void writeTimestampFile(StringRef TimestampFile) {
   raw_fd_ostream Out(TimestampFile.str(), EC, sys::fs::F_None);
 }
 
+static Expected<std::chrono::seconds> parseDuration(StringRef Duration) {
+  if (Duration.empty())
+    return make_error<StringError>("Duration must not be empty",
+                                   inconvertibleErrorCode());
+
+  StringRef NumStr = Duration.slice(0, Duration.size()-1);
+  uint64_t Num;
+  if (NumStr.getAsInteger(0, Num))
+    return make_error<StringError>("'" + NumStr + "' not an integer",
+                                   inconvertibleErrorCode());
+
+  switch (Duration.back()) {
+  case 's':
+    return std::chrono::seconds(Num);
+  case 'm':
+    return std::chrono::minutes(Num);
+  case 'h':
+    return std::chrono::hours(Num);
+  default:
+    return make_error<StringError>("'" + Duration +
+                                       "' must end with one of 's', 'm' or 'h'",
+                                   inconvertibleErrorCode());
+  }
+}
+
+Expected<CachePruningPolicy>
+llvm::parseCachePruningPolicy(StringRef PolicyStr) {
+  CachePruningPolicy Policy;
+  std::pair<StringRef, StringRef> P = {"", PolicyStr};
+  while (!P.second.empty()) {
+    P = P.second.split(':');
+
+    StringRef Key, Value;
+    std::tie(Key, Value) = P.first.split('=');
+    if (Key == "prune_interval") {
+      auto DurationOrErr = parseDuration(Value);
+      if (!DurationOrErr)
+        return std::move(DurationOrErr.takeError());
+      Policy.Interval = *DurationOrErr;
+    } else if (Key == "prune_after") {
+      auto DurationOrErr = parseDuration(Value);
+      if (!DurationOrErr)
+        return std::move(DurationOrErr.takeError());
+      Policy.Expiration = *DurationOrErr;
+    } else if (Key == "cache_size") {
+      if (Value.back() != '%')
+        return make_error<StringError>("'" + Value + "' must be a percentage",
+                                       inconvertibleErrorCode());
+      StringRef SizeStr = Value.slice(0, Value.size() - 1);
+      uint64_t Size;
+      if (SizeStr.getAsInteger(0, Size))
+        return make_error<StringError>("'" + SizeStr + "' not an integer",
+                                       inconvertibleErrorCode());
+      if (Size > 100)
+        return make_error<StringError>("'" + SizeStr +
+                                           "' must be between 0 and 100",
+                                       inconvertibleErrorCode());
+      Policy.PercentageOfAvailableSpace = Size;
+    } else {
+      return make_error<StringError>("Unknown key: '" + Key + "'",
+                                     inconvertibleErrorCode());
+    }
+  }
+
+  return Policy;
+}
+
 /// Prune the cache of files that haven't been accessed in a long time.
 bool llvm::pruneCache(StringRef Path, CachePruningPolicy Policy) {
   using namespace std::chrono;
index ed84ee9..a7be18b 100644 (file)
@@ -10,6 +10,7 @@ add_llvm_unittest(SupportTests
   BinaryStreamTest.cpp
   BlockFrequencyTest.cpp
   BranchProbabilityTest.cpp
+  CachePruningTest.cpp
   Casting.cpp
   Chrono.cpp
   CommandLineTest.cpp
diff --git a/unittests/Support/CachePruningTest.cpp b/unittests/Support/CachePruningTest.cpp
new file mode 100644 (file)
index 0000000..04ac0d0
--- /dev/null
@@ -0,0 +1,71 @@
+//===- CachePruningTest.cpp -----------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/CachePruning.h"
+#include "llvm/Support/Error.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+TEST(CachePruningPolicyParser, Empty) {
+  auto P = parseCachePruningPolicy("");
+  ASSERT_TRUE(bool(P));
+  EXPECT_EQ(std::chrono::seconds(1200), P->Interval);
+  EXPECT_EQ(std::chrono::hours(7 * 24), P->Expiration);
+  EXPECT_EQ(75u, P->PercentageOfAvailableSpace);
+}
+
+TEST(CachePruningPolicyParser, Interval) {
+  auto P = parseCachePruningPolicy("prune_interval=1s");
+  ASSERT_TRUE(bool(P));
+  EXPECT_EQ(std::chrono::seconds(1), P->Interval);
+  P = parseCachePruningPolicy("prune_interval=2m");
+  ASSERT_TRUE(bool(P));
+  EXPECT_EQ(std::chrono::minutes(2), P->Interval);
+  P = parseCachePruningPolicy("prune_interval=3h");
+  ASSERT_TRUE(bool(P));
+  EXPECT_EQ(std::chrono::hours(3), P->Interval);
+}
+
+TEST(CachePruningPolicyParser, Expiration) {
+  auto P = parseCachePruningPolicy("prune_after=1s");
+  ASSERT_TRUE(bool(P));
+  EXPECT_EQ(std::chrono::seconds(1), P->Expiration);
+}
+
+TEST(CachePruningPolicyParser, PercentageOfAvailableSpace) {
+  auto P = parseCachePruningPolicy("cache_size=100%");
+  ASSERT_TRUE(bool(P));
+  EXPECT_EQ(100u, P->PercentageOfAvailableSpace);
+}
+
+TEST(CachePruningPolicyParser, Multiple) {
+  auto P = parseCachePruningPolicy("prune_after=1s:cache_size=50%");
+  ASSERT_TRUE(bool(P));
+  EXPECT_EQ(std::chrono::seconds(1200), P->Interval);
+  EXPECT_EQ(std::chrono::seconds(1), P->Expiration);
+  EXPECT_EQ(50u, P->PercentageOfAvailableSpace);
+}
+
+TEST(CachePruningPolicyParser, Errors) {
+  EXPECT_EQ("Duration must not be empty",
+            toString(parseCachePruningPolicy("prune_interval=").takeError()));
+  EXPECT_EQ("'foo' not an integer",
+            toString(parseCachePruningPolicy("prune_interval=foos").takeError()));
+  EXPECT_EQ("'24x' must end with one of 's', 'm' or 'h'",
+            toString(parseCachePruningPolicy("prune_interval=24x").takeError()));
+  EXPECT_EQ("'foo' must be a percentage",
+            toString(parseCachePruningPolicy("cache_size=foo").takeError()));
+  EXPECT_EQ("'foo' not an integer",
+            toString(parseCachePruningPolicy("cache_size=foo%").takeError()));
+  EXPECT_EQ("'101' must be between 0 and 100",
+            toString(parseCachePruningPolicy("cache_size=101%").takeError()));
+  EXPECT_EQ("Unknown key: 'foo'",
+            toString(parseCachePruningPolicy("foo=bar").takeError()));
+}