EGL Multifile Blobcache: Add status file
Add a status file that contains the cache version and
platform build ID. On future startups, if those values
don't match, clear the cache.
This alleviates a problem on driver updates, which cause
all new queries to miss, creating new entires, filling the
cache. For apps with many small entries, the start up
time has become a problem.
Test: libEGL_test, EGL_test, ANGLE trace tests, apps
Bug: b/295051628
Bug: b/310535559
Change-Id: I17b91fb6c994237fb5c2a220db4f23050d45742b
diff --git a/opengl/libs/EGL/MultifileBlobCache_test.cpp b/opengl/libs/EGL/MultifileBlobCache_test.cpp
index 8e27f5a..90a0f1e 100644
--- a/opengl/libs/EGL/MultifileBlobCache_test.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache_test.cpp
@@ -16,13 +16,17 @@
#include "MultifileBlobCache.h"
+#include <android-base/properties.h>
#include <android-base/test_utils.h>
#include <fcntl.h>
#include <gtest/gtest.h>
#include <stdio.h>
+#include <fstream>
#include <memory>
+using namespace std::literals;
+
namespace android {
template <typename T>
@@ -36,19 +40,35 @@
class MultifileBlobCacheTest : public ::testing::Test {
protected:
virtual void SetUp() {
+ clearProperties();
mTempFile.reset(new TemporaryFile());
mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize,
kMaxTotalEntries, &mTempFile->path[0]));
}
- virtual void TearDown() { mMBC.reset(); }
+ virtual void TearDown() {
+ clearProperties();
+ mMBC.reset();
+ }
int getFileDescriptorCount();
+ std::vector<std::string> getCacheEntries();
+
+ void clearProperties();
std::unique_ptr<TemporaryFile> mTempFile;
std::unique_ptr<MultifileBlobCache> mMBC;
};
+void MultifileBlobCacheTest::clearProperties() {
+ // Clear any debug properties used in the tests
+ base::SetProperty("debug.egl.blobcache.cache_version", "");
+ base::WaitForProperty("debug.egl.blobcache.cache_version", "");
+
+ base::SetProperty("debug.egl.blobcache.build_id", "");
+ base::WaitForProperty("debug.egl.blobcache.build_id", "");
+}
+
TEST_F(MultifileBlobCacheTest, CacheSingleValueSucceeds) {
unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mMBC->set("abcd", 4, "efgh", 4);
@@ -287,4 +307,200 @@
ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
}
+std::vector<std::string> MultifileBlobCacheTest::getCacheEntries() {
+ std::string cachePath = &mTempFile->path[0];
+ std::string multifileDirName = cachePath + ".multifile";
+ std::vector<std::string> cacheEntries;
+
+ struct stat info;
+ if (stat(multifileDirName.c_str(), &info) == 0) {
+ // We have a multifile dir. Skip the status file and return the only entry.
+ DIR* dir;
+ struct dirent* entry;
+ if ((dir = opendir(multifileDirName.c_str())) != nullptr) {
+ while ((entry = readdir(dir)) != nullptr) {
+ if (entry->d_name == "."s || entry->d_name == ".."s) {
+ continue;
+ }
+ if (strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) {
+ continue;
+ }
+ cacheEntries.push_back(multifileDirName + "/" + entry->d_name);
+ }
+ } else {
+ printf("Unable to open %s, error: %s\n", multifileDirName.c_str(),
+ std::strerror(errno));
+ }
+ } else {
+ printf("Unable to stat %s, error: %s\n", multifileDirName.c_str(), std::strerror(errno));
+ }
+
+ return cacheEntries;
+}
+
+TEST_F(MultifileBlobCacheTest, CacheContainsStatus) {
+ struct stat info;
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+
+ // After INIT, cache should have a status
+ ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0);
+
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure status lives after closing the cache
+ ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0);
+
+ // Open the cache again
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we still have a status
+ ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0);
+}
+
+// Verify missing cache status file causes cache the be cleared
+TEST_F(MultifileBlobCacheTest, MissingCacheStatusClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Delete the status file
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+ remove(statusFile.str().c_str());
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify modified cache status file BEGIN causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, ModifiedCacheStatusBeginClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Modify the status file
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+
+ // Stomp on the beginning of the cache file
+ const char* stomp = "BADF00D";
+ std::fstream fs(statusFile.str());
+ fs.seekp(0, std::ios_base::beg);
+ fs.write(stomp, strlen(stomp));
+ fs.flush();
+ fs.close();
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify modified cache status file END causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, ModifiedCacheStatusEndClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Modify the status file
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+
+ // Stomp on the END of the cache status file, modifying its contents
+ const char* stomp = "BADF00D";
+ std::fstream fs(statusFile.str());
+ fs.seekp(-strlen(stomp), std::ios_base::end);
+ fs.write(stomp, strlen(stomp));
+ fs.flush();
+ fs.close();
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify mismatched cacheVersion causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, MismatchedCacheVersionClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Set a debug cacheVersion
+ std::string newCacheVersion = std::to_string(kMultifileBlobCacheVersion + 1);
+ ASSERT_TRUE(base::SetProperty("debug.egl.blobcache.cache_version", newCacheVersion.c_str()));
+ ASSERT_TRUE(
+ base::WaitForProperty("debug.egl.blobcache.cache_version", newCacheVersion.c_str()));
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify mismatched buildId causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, MismatchedBuildIdClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Set a debug buildId
+ base::SetProperty("debug.egl.blobcache.build_id", "foo");
+ base::WaitForProperty("debug.egl.blobcache.build_id", "foo");
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
} // namespace android