1 /*
2 * Copyright © 2015 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 /* A collection of unit tests for cache.c */
25
26 #include <gtest/gtest.h>
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdbool.h>
31 #include <string.h>
32 #include <ftw.h>
33 #include <errno.h>
34 #include <stdarg.h>
35 #include <inttypes.h>
36 #include <limits.h>
37 #include <time.h>
38 #include <unistd.h>
39
40 #include "util/detect_os.h"
41 #include "util/mesa-sha1.h"
42 #include "util/disk_cache.h"
43 #include "util/disk_cache_os.h"
44 #include "util/ralloc.h"
45
46 #ifdef ENABLE_SHADER_CACHE
47
48 /* Callback for nftw used in rmrf_local below.
49 */
50 static int
remove_entry(const char * path,const struct stat * sb,int typeflag,struct FTW * ftwbuf)51 remove_entry(const char *path,
52 const struct stat *sb,
53 int typeflag,
54 struct FTW *ftwbuf)
55 {
56 int err = remove(path);
57
58 if (err)
59 fprintf(stderr, "Error removing %s: %s\n", path, strerror(errno));
60
61 return err;
62 }
63
64 /* Recursively remove a directory.
65 *
66 * This is equivalent to "rm -rf <dir>" with one bit of protection
67 * that the directory name must begin with "." to ensure we don't
68 * wander around deleting more than intended.
69 *
70 * Returns 0 on success, -1 on any error.
71 */
72 static int
rmrf_local(const char * path)73 rmrf_local(const char *path)
74 {
75 if (path == NULL || *path == '\0' || *path != '.')
76 return -1;
77
78 return nftw(path, remove_entry, 64, FTW_DEPTH | FTW_PHYS);
79 }
80
81 static void
check_directories_created(void * mem_ctx,const char * cache_dir)82 check_directories_created(void *mem_ctx, const char *cache_dir)
83 {
84 bool sub_dirs_created = false;
85
86 char buf[PATH_MAX];
87 if (getcwd(buf, PATH_MAX)) {
88 char *full_path = ralloc_asprintf(mem_ctx, "%s%s", buf, ++cache_dir);
89 struct stat sb;
90 if (stat(full_path, &sb) != -1 && S_ISDIR(sb.st_mode))
91 sub_dirs_created = true;
92 }
93
94 EXPECT_TRUE(sub_dirs_created) << "create sub dirs";
95 }
96
97 static bool
does_cache_contain(struct disk_cache * cache,const cache_key key)98 does_cache_contain(struct disk_cache *cache, const cache_key key)
99 {
100 void *result;
101
102 result = disk_cache_get(cache, key, NULL);
103
104 if (result) {
105 free(result);
106 return true;
107 }
108
109 return false;
110 }
111
112 static bool
cache_exists(struct disk_cache * cache)113 cache_exists(struct disk_cache *cache)
114 {
115 uint8_t key[20];
116 char data[] = "some test data";
117
118 if (!cache)
119 return false;
120
121 disk_cache_compute_key(cache, data, sizeof(data), key);
122 disk_cache_put(cache, key, data, sizeof(data), NULL);
123 disk_cache_wait_for_idle(cache);
124 void *result = disk_cache_get(cache, key, NULL);
125 disk_cache_remove(cache, key);
126
127 free(result);
128 return result != NULL;
129 }
130
131 static void *
poll_disk_cache_get(struct disk_cache * cache,const cache_key key,size_t * size)132 poll_disk_cache_get(struct disk_cache *cache,
133 const cache_key key,
134 size_t *size)
135 {
136 void *result;
137
138 for (int iter = 0; iter < 1000; ++iter) {
139 result = disk_cache_get(cache, key, size);
140 if (result)
141 return result;
142
143 usleep(1000);
144 }
145
146 return NULL;
147 }
148
149 #define CACHE_TEST_TMP "./cache-test-tmp"
150
151 static void
test_disk_cache_create(void * mem_ctx,const char * cache_dir_name,const char * driver_id)152 test_disk_cache_create(void *mem_ctx, const char *cache_dir_name,
153 const char *driver_id)
154 {
155 struct disk_cache *cache;
156 int err;
157
158 /* Before doing anything else, ensure that with
159 * MESA_SHADER_CACHE_DISABLE set to true, that disk_cache_create returns NO-OP cache.
160 */
161 setenv("MESA_SHADER_CACHE_DISABLE", "true", 1);
162 cache = disk_cache_create("test", driver_id, 0);
163 EXPECT_EQ(cache->type, DISK_CACHE_NONE) << "disk_cache_create with MESA_SHADER_CACHE_DISABLE set";
164 disk_cache_destroy(cache);
165
166 unsetenv("MESA_SHADER_CACHE_DISABLE");
167
168 #ifdef SHADER_CACHE_DISABLE_BY_DEFAULT
169 /* With SHADER_CACHE_DISABLE_BY_DEFAULT, ensure that with
170 * MESA_SHADER_CACHE_DISABLE set to nothing, disk_cache_create returns NO-OP cache.
171 */
172 unsetenv("MESA_SHADER_CACHE_DISABLE");
173 cache = disk_cache_create("test", driver_id, 0);
174 EXPECT_EQ(cache->type, DISK_CACHE_NONE)
175 << "disk_cache_create with MESA_SHADER_CACHE_DISABLE unset "
176 "and SHADER_CACHE_DISABLE_BY_DEFAULT build option";
177 disk_cache_destroy(cache);
178
179 /* For remaining tests, ensure that the cache is enabled. */
180 setenv("MESA_SHADER_CACHE_DISABLE", "false", 1);
181 #endif /* SHADER_CACHE_DISABLE_BY_DEFAULT */
182
183 /* For the first real disk_cache_create() clear these environment
184 * variables to test creation of cache in home directory.
185 */
186 unsetenv("MESA_SHADER_CACHE_DIR");
187 unsetenv("XDG_CACHE_HOME");
188
189 cache = disk_cache_create("test", driver_id, 0);
190 EXPECT_NE(cache, nullptr) << "disk_cache_create with no environment variables";
191
192 disk_cache_destroy(cache);
193
194 #if DETECT_OS_ANDROID
195 /* Android doesn't try writing to disk (just calls the cache callbacks), so
196 * the directory tests below don't apply.
197 */
198 return;
199 #endif
200
201 /* Test with XDG_CACHE_HOME set */
202 setenv("XDG_CACHE_HOME", CACHE_TEST_TMP "/xdg-cache-home", 1);
203 cache = disk_cache_create("test", driver_id, 0);
204 EXPECT_FALSE(cache_exists(cache))
205 << "disk_cache_create with XDG_CACHE_HOME set with a non-existing parent directory";
206
207 err = mkdir(CACHE_TEST_TMP, 0755);
208 if (err != 0) {
209 fprintf(stderr, "Error creating %s: %s\n", CACHE_TEST_TMP, strerror(errno));
210 GTEST_FAIL();
211 }
212 disk_cache_destroy(cache);
213
214 cache = disk_cache_create("test", driver_id, 0);
215 EXPECT_TRUE(cache_exists(cache))
216 << "disk_cache_create with XDG_CACHE_HOME set";
217
218 char *path = ralloc_asprintf(
219 mem_ctx, "%s%s", CACHE_TEST_TMP "/xdg-cache-home/", cache_dir_name);
220 check_directories_created(mem_ctx, path);
221
222 disk_cache_destroy(cache);
223
224 /* Test with MESA_SHADER_CACHE_DIR set */
225 err = rmrf_local(CACHE_TEST_TMP);
226 EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP;
227
228 setenv("MESA_SHADER_CACHE_DIR", CACHE_TEST_TMP "/mesa-shader-cache-dir", 1);
229 cache = disk_cache_create("test", driver_id, 0);
230 EXPECT_TRUE(cache_exists(cache))
231 << "disk_cache_create with MESA_SHADER_CACHE_DIR set with a non-existing parent directory";
232
233 disk_cache_destroy(cache);
234 rmrf_local(CACHE_TEST_TMP);
235 EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP;
236
237 err = mkdir(CACHE_TEST_TMP, 0755);
238 if (err != 0) {
239 fprintf(stderr, "Error creating %s: %s\n", CACHE_TEST_TMP, strerror(errno));
240 GTEST_FAIL();
241 }
242
243 cache = disk_cache_create("test", driver_id, 0);
244 EXPECT_TRUE(cache_exists(cache)) << "disk_cache_create with MESA_SHADER_CACHE_DIR set with existing parent directory";
245
246 path = ralloc_asprintf(
247 mem_ctx, "%s%s", CACHE_TEST_TMP "/mesa-shader-cache-dir/", cache_dir_name);
248 check_directories_created(mem_ctx, path);
249
250 disk_cache_destroy(cache);
251 }
252
253 static void
test_put_and_get(bool test_cache_size_limit,const char * driver_id)254 test_put_and_get(bool test_cache_size_limit, const char *driver_id)
255 {
256 struct disk_cache *cache;
257 char blob[] = "This is a blob of thirty-seven bytes";
258 uint8_t blob_key[20];
259 char string[] = "While this string has thirty-four";
260 uint8_t string_key[20];
261 char *result;
262 size_t size;
263 uint8_t *one_KB, *one_MB;
264 uint8_t one_KB_key[20], one_MB_key[20];
265 int count;
266
267 #ifdef SHADER_CACHE_DISABLE_BY_DEFAULT
268 setenv("MESA_SHADER_CACHE_DISABLE", "false", 1);
269 #endif /* SHADER_CACHE_DISABLE_BY_DEFAULT */
270
271 cache = disk_cache_create("test", driver_id, 0);
272
273 disk_cache_compute_key(cache, blob, sizeof(blob), blob_key);
274
275 /* Ensure that disk_cache_get returns nothing before anything is added. */
276 result = (char *) disk_cache_get(cache, blob_key, &size);
277 EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
278 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
279
280 /* Simple test of put and get. */
281 disk_cache_put(cache, blob_key, blob, sizeof(blob), NULL);
282
283 /* disk_cache_put() hands things off to a thread so wait for it. */
284 disk_cache_wait_for_idle(cache);
285
286 result = (char *) disk_cache_get(cache, blob_key, &size);
287 EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
288 EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
289
290 free(result);
291
292 /* Test put and get of a second item. */
293 disk_cache_compute_key(cache, string, sizeof(string), string_key);
294 disk_cache_put(cache, string_key, string, sizeof(string), NULL);
295
296 /* disk_cache_put() hands things off to a thread so wait for it. */
297 disk_cache_wait_for_idle(cache);
298
299 result = (char *) disk_cache_get(cache, string_key, &size);
300 EXPECT_STREQ(result, string) << "2nd disk_cache_get of existing item (pointer)";
301 EXPECT_EQ(size, sizeof(string)) << "2nd disk_cache_get of existing item (size)";
302
303 free(result);
304
305 /* Set the cache size to 1KB and add a 1KB item to force an eviction. */
306 disk_cache_destroy(cache);
307
308 if (!test_cache_size_limit)
309 return;
310
311 setenv("MESA_SHADER_CACHE_MAX_SIZE", "1K", 1);
312 cache = disk_cache_create("test", driver_id, 0);
313
314 one_KB = (uint8_t *) calloc(1, 1024);
315
316 /* Obviously the SHA-1 hash of 1024 zero bytes isn't particularly
317 * interesting. But we do have want to take some special care with
318 * the hash we use here. The issue is that in this artificial case,
319 * (with only three files in the cache), the probability is good
320 * that each of the three files will end up in their own
321 * directory. Then, if the directory containing the .tmp file for
322 * the new item being added for disk_cache_put() is the chosen victim
323 * directory for eviction, then no suitable file will be found and
324 * nothing will be evicted.
325 *
326 * That's actually expected given how the eviction code is
327 * implemented, (which expects to only evict once things are more
328 * interestingly full than that).
329 *
330 * For this test, we force this signature to land in the same
331 * directory as the original blob first written to the cache.
332 */
333 disk_cache_compute_key(cache, one_KB, 1024, one_KB_key);
334 one_KB_key[0] = blob_key[0];
335
336 disk_cache_put(cache, one_KB_key, one_KB, 1024, NULL);
337
338 free(one_KB);
339
340 /* disk_cache_put() hands things off to a thread so wait for it. */
341 disk_cache_wait_for_idle(cache);
342
343 result = (char *) disk_cache_get(cache, one_KB_key, &size);
344 EXPECT_NE(result, nullptr) << "3rd disk_cache_get of existing item (pointer)";
345 EXPECT_EQ(size, 1024) << "3rd disk_cache_get of existing item (size)";
346
347 free(result);
348
349 /* Ensure eviction happened by checking that both of the previous
350 * cache itesm were evicted.
351 */
352 bool contains_1KB_file = false;
353 count = 0;
354 if (does_cache_contain(cache, blob_key))
355 count++;
356
357 if (does_cache_contain(cache, string_key))
358 count++;
359
360 if (does_cache_contain(cache, one_KB_key)) {
361 count++;
362 contains_1KB_file = true;
363 }
364
365 EXPECT_TRUE(contains_1KB_file)
366 << "disk_cache_put eviction last file == MAX_SIZE (1KB)";
367 EXPECT_EQ(count, 1) << "disk_cache_put eviction with MAX_SIZE=1K";
368
369 /* Now increase the size to 1M, add back both items, and ensure all
370 * three that have been added are available via disk_cache_get.
371 */
372 disk_cache_destroy(cache);
373
374 setenv("MESA_SHADER_CACHE_MAX_SIZE", "1M", 1);
375 cache = disk_cache_create("test", driver_id, 0);
376
377 disk_cache_put(cache, blob_key, blob, sizeof(blob), NULL);
378 disk_cache_put(cache, string_key, string, sizeof(string), NULL);
379
380 /* disk_cache_put() hands things off to a thread so wait for it. */
381 disk_cache_wait_for_idle(cache);
382
383 count = 0;
384 if (does_cache_contain(cache, blob_key))
385 count++;
386
387 if (does_cache_contain(cache, string_key))
388 count++;
389
390 if (does_cache_contain(cache, one_KB_key))
391 count++;
392
393 EXPECT_EQ(count, 3) << "no eviction before overflow with MAX_SIZE=1M";
394
395 /* Finally, check eviction again after adding an object of size 1M. */
396 one_MB = (uint8_t *) calloc(1024, 1024);
397
398 disk_cache_compute_key(cache, one_MB, 1024 * 1024, one_MB_key);
399 one_MB_key[0] = blob_key[0];
400
401 disk_cache_put(cache, one_MB_key, one_MB, 1024 * 1024, NULL);
402
403 free(one_MB);
404
405 /* disk_cache_put() hands things off to a thread so wait for it. */
406 disk_cache_wait_for_idle(cache);
407
408 bool contains_1MB_file = false;
409 count = 0;
410 if (does_cache_contain(cache, blob_key))
411 count++;
412
413 if (does_cache_contain(cache, string_key))
414 count++;
415
416 if (does_cache_contain(cache, one_KB_key))
417 count++;
418
419 if (does_cache_contain(cache, one_MB_key)) {
420 count++;
421 contains_1MB_file = true;
422 }
423
424 EXPECT_TRUE(contains_1MB_file)
425 << "disk_cache_put eviction last file == MAX_SIZE (1MB)";
426 EXPECT_EQ(count, 1) << "eviction after overflow with MAX_SIZE=1M";
427
428 disk_cache_destroy(cache);
429 }
430
431 static void
test_put_key_and_get_key(const char * driver_id)432 test_put_key_and_get_key(const char *driver_id)
433 {
434 struct disk_cache *cache;
435 bool result;
436
437 uint8_t key_a[20] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
438 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
439 uint8_t key_b[20] = { 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
440 30, 33, 32, 33, 34, 35, 36, 37, 38, 39};
441 uint8_t key_a_collide[20] =
442 { 0, 1, 42, 43, 44, 45, 46, 47, 48, 49,
443 50, 55, 52, 53, 54, 55, 56, 57, 58, 59};
444
445 #ifdef SHADER_CACHE_DISABLE_BY_DEFAULT
446 setenv("MESA_SHADER_CACHE_DISABLE", "false", 1);
447 #endif /* SHADER_CACHE_DISABLE_BY_DEFAULT */
448
449 cache = disk_cache_create("test", driver_id, 0);
450
451 /* First test that disk_cache_has_key returns false before disk_cache_put_key */
452 result = disk_cache_has_key(cache, key_a);
453 EXPECT_EQ(result, 0) << "disk_cache_has_key before key added";
454
455 /* Then a couple of tests of disk_cache_put_key followed by disk_cache_has_key */
456 disk_cache_put_key(cache, key_a);
457 result = disk_cache_has_key(cache, key_a);
458 EXPECT_EQ(result, 1) << "disk_cache_has_key after key added";
459
460 disk_cache_put_key(cache, key_b);
461 result = disk_cache_has_key(cache, key_b);
462 EXPECT_EQ(result, 1) << "2nd disk_cache_has_key after key added";
463
464 /* Test that a key with the same two bytes as an existing key
465 * forces an eviction.
466 */
467 disk_cache_put_key(cache, key_a_collide);
468 result = disk_cache_has_key(cache, key_a_collide);
469 EXPECT_EQ(result, 1) << "put_key of a colliding key lands in the cache";
470
471 result = disk_cache_has_key(cache, key_a);
472 EXPECT_EQ(result, 0) << "put_key of a colliding key evicts from the cache";
473
474 /* And finally test that we can re-add the original key to re-evict
475 * the colliding key.
476 */
477 disk_cache_put_key(cache, key_a);
478 result = disk_cache_has_key(cache, key_a);
479 EXPECT_EQ(result, 1) << "put_key of original key lands again";
480
481 result = disk_cache_has_key(cache, key_a_collide);
482 EXPECT_EQ(result, 0) << "put_key of orginal key evicts the colliding key";
483
484 disk_cache_destroy(cache);
485 }
486
487 /* To make sure we are not just using the inmemory cache index for the single
488 * file cache we test adding and retriving cache items between two different
489 * cache instances.
490 */
491 static void
test_put_and_get_between_instances(const char * driver_id)492 test_put_and_get_between_instances(const char *driver_id)
493 {
494 char blob[] = "This is a blob of thirty-seven bytes";
495 uint8_t blob_key[20];
496 char string[] = "While this string has thirty-four";
497 uint8_t string_key[20];
498 char *result;
499 size_t size;
500
501 #ifdef SHADER_CACHE_DISABLE_BY_DEFAULT
502 setenv("MESA_SHADER_CACHE_DISABLE", "false", 1);
503 #endif /* SHADER_CACHE_DISABLE_BY_DEFAULT */
504
505 struct disk_cache *cache1 = disk_cache_create("test_between_instances",
506 driver_id, 0);
507 struct disk_cache *cache2 = disk_cache_create("test_between_instances",
508 driver_id, 0);
509
510 disk_cache_compute_key(cache1, blob, sizeof(blob), blob_key);
511
512 /* Ensure that disk_cache_get returns nothing before anything is added. */
513 result = (char *) disk_cache_get(cache1, blob_key, &size);
514 EXPECT_EQ(result, nullptr) << "disk_cache_get(cache1) with non-existent item (pointer)";
515 EXPECT_EQ(size, 0) << "disk_cache_get(cach1) with non-existent item (size)";
516
517 result = (char *) disk_cache_get(cache2, blob_key, &size);
518 EXPECT_EQ(result, nullptr) << "disk_cache_get(cache2) with non-existent item (pointer)";
519 EXPECT_EQ(size, 0) << "disk_cache_get(cache2) with non-existent item (size)";
520
521 /* Simple test of put and get. */
522 disk_cache_put(cache1, blob_key, blob, sizeof(blob), NULL);
523
524 /* disk_cache_put() hands things off to a thread so wait for it. */
525 disk_cache_wait_for_idle(cache1);
526
527 result = (char *) disk_cache_get(cache2, blob_key, &size);
528 EXPECT_STREQ(blob, result) << "disk_cache_get(cache2) of existing item (pointer)";
529 EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of(cache2) existing item (size)";
530
531 free(result);
532
533 /* Test put and get of a second item, via the opposite instances */
534 disk_cache_compute_key(cache2, string, sizeof(string), string_key);
535 disk_cache_put(cache2, string_key, string, sizeof(string), NULL);
536
537 /* disk_cache_put() hands things off to a thread so wait for it. */
538 disk_cache_wait_for_idle(cache2);
539
540 result = (char *) disk_cache_get(cache1, string_key, &size);
541 EXPECT_STREQ(result, string) << "2nd disk_cache_get(cache1) of existing item (pointer)";
542 EXPECT_EQ(size, sizeof(string)) << "2nd disk_cache_get(cache1) of existing item (size)";
543
544 free(result);
545
546 disk_cache_destroy(cache1);
547 disk_cache_destroy(cache2);
548 }
549
550 static void
test_put_and_get_between_instances_with_eviction(const char * driver_id)551 test_put_and_get_between_instances_with_eviction(const char *driver_id)
552 {
553 cache_key small_key[8], small_key2, big_key[2];
554 struct disk_cache *cache[2];
555 unsigned int i, n, k;
556 uint8_t *small;
557 uint8_t *big;
558 char *result;
559 size_t size;
560
561 #ifdef SHADER_CACHE_DISABLE_BY_DEFAULT
562 setenv("MESA_SHADER_CACHE_DISABLE", "false", 1);
563 #endif /* SHADER_CACHE_DISABLE_BY_DEFAULT */
564
565 setenv("MESA_SHADER_CACHE_MAX_SIZE", "2K", 1);
566
567 cache[0] = disk_cache_create("test_between_instances_with_eviction", driver_id, 0);
568 cache[1] = disk_cache_create("test_between_instances_with_eviction", driver_id, 0);
569
570 uint8_t two_KB[2048] = { 0 };
571 cache_key two_KB_key = { 'T', 'W', 'O', 'K', 'B' };
572
573 /* Flush the database by adding the dummy 2KB entry */
574 disk_cache_put(cache[0], two_KB_key, two_KB, sizeof(two_KB), NULL);
575 disk_cache_wait_for_idle(cache[0]);
576
577 int size_big = 1000;
578 size_big -= sizeof(struct cache_entry_file_data);
579 size_big -= mesa_cache_db_file_entry_size();
580 size_big -= cache[0]->driver_keys_blob_size;
581 size_big -= 4 + 8; /* cache_item_metadata size + room for alignment */
582
583 for (i = 0; i < ARRAY_SIZE(big_key); i++) {
584 big = (uint8_t *) malloc(size_big);
585 memset(big, i, size_big);
586
587 disk_cache_compute_key(cache[0], big, size_big, big_key[i]);
588 disk_cache_put(cache[0], big_key[i], big, size_big, NULL);
589 disk_cache_wait_for_idle(cache[0]);
590
591 result = (char *) disk_cache_get(cache[0], big_key[i], &size);
592 EXPECT_NE(result, nullptr) << "disk_cache_get with existent item (pointer)";
593 EXPECT_EQ(size, size_big) << "disk_cache_get with existent item (size)";
594 free(result);
595
596 free(big);
597 }
598
599 int size_small = 256;
600 size_small -= sizeof(struct cache_entry_file_data);
601 size_small -= mesa_cache_db_file_entry_size();
602 size_small -= cache[1]->driver_keys_blob_size;
603 size_small -= 4 + 8; /* cache_item_metadata size + room for alignment */
604
605 for (i = 0; i < ARRAY_SIZE(small_key); i++) {
606 small = (uint8_t *) malloc(size_small);
607 memset(small, i, size_small);
608
609 disk_cache_compute_key(cache[1], small, size_small, small_key[i]);
610 disk_cache_put(cache[1], small_key[i], small, size_small, NULL);
611 disk_cache_wait_for_idle(cache[1]);
612
613 /*
614 * At first we added two 1000KB entries to cache[0]. Now, when first
615 * 256KB entry is added, the two 1000KB entries are evicted because
616 * at minimum cache_max_size/2 is evicted on overflow.
617 *
618 * All four 256KB entries stay in the cache.
619 */
620 for (k = 0; k < ARRAY_SIZE(cache); k++) {
621 for (n = 0; n <= i; n++) {
622 result = (char *) disk_cache_get(cache[k], big_key[0], &size);
623 EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
624 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
625 free(result);
626
627 result = (char *) disk_cache_get(cache[k], big_key[1], &size);
628 EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
629 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
630 free(result);
631
632 result = (char *) disk_cache_get(cache[k], small_key[n], &size);
633 EXPECT_NE(result, nullptr) << "disk_cache_get of existing item (pointer)";
634 EXPECT_EQ(size, size_small) << "disk_cache_get of existing item (size)";
635 free(result);
636
637 result = (char *) disk_cache_get(cache[k], small_key[n], &size);
638 EXPECT_NE(result, nullptr) << "disk_cache_get of existing item (pointer)";
639 EXPECT_EQ(size, size_small) << "disk_cache_get of existing item (size)";
640 free(result);
641 }
642 }
643
644 free(small);
645 }
646
647 small = (uint8_t *) malloc(size_small);
648 memset(small, i, size_small);
649
650 /* Add another 256KB entry. This will evict first five 256KB entries
651 * of eight that we added previously. */
652 disk_cache_compute_key(cache[0], small, size_small, small_key2);
653 disk_cache_put(cache[0], small_key2, small, size_small, NULL);
654 disk_cache_wait_for_idle(cache[0]);
655
656 free(small);
657
658 for (k = 0; k < ARRAY_SIZE(cache); k++) {
659 result = (char *) disk_cache_get(cache[k], small_key2, &size);
660 EXPECT_NE(result, nullptr) << "disk_cache_get of existing item (pointer)";
661 EXPECT_EQ(size, size_small) << "disk_cache_get of existing item (size)";
662 free(result);
663 }
664
665 for (i = 0, k = 0; k < ARRAY_SIZE(cache); k++) {
666 for (n = 0; n < ARRAY_SIZE(small_key); n++) {
667 result = (char *) disk_cache_get(cache[k], small_key[n], &size);
668 if (!result)
669 i++;
670 free(result);
671 }
672 }
673
674 EXPECT_EQ(i, 10) << "2x disk_cache_get with 5 non-existent 256KB items";
675
676 disk_cache_destroy(cache[0]);
677 disk_cache_destroy(cache[1]);
678 }
679 #endif /* ENABLE_SHADER_CACHE */
680
681 class Cache : public ::testing::Test {
682 protected:
683 void *mem_ctx;
684
Cache()685 Cache() {
686 mem_ctx = ralloc_context(NULL);
687 }
~Cache()688 ~Cache() {
689 ralloc_free(mem_ctx);
690 }
691 };
692
TEST_F(Cache,MultiFile)693 TEST_F(Cache, MultiFile)
694 {
695 const char *driver_id;
696
697 #ifndef ENABLE_SHADER_CACHE
698 GTEST_SKIP() << "ENABLE_SHADER_CACHE not defined.";
699 #else
700 bool compress = true;
701
702 run_tests:
703 if (!compress)
704 driver_id = "make_check_uncompressed";
705 else
706 driver_id = "make_check";
707
708 test_disk_cache_create(mem_ctx, CACHE_DIR_NAME, driver_id);
709
710 test_put_and_get(true, driver_id);
711
712 test_put_key_and_get_key(driver_id);
713
714 int err = rmrf_local(CACHE_TEST_TMP);
715 EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again";
716
717 if (compress) {
718 compress = false;
719 goto run_tests;
720 }
721 #endif
722 }
723
TEST_F(Cache,SingleFile)724 TEST_F(Cache, SingleFile)
725 {
726 const char *driver_id;
727
728 #ifndef ENABLE_SHADER_CACHE
729 GTEST_SKIP() << "ENABLE_SHADER_CACHE not defined.";
730 #else
731 bool compress = true;
732
733 run_tests:
734 setenv("MESA_DISK_CACHE_SINGLE_FILE", "true", 1);
735
736 if (!compress)
737 driver_id = "make_check_uncompressed";
738 else
739 driver_id = "make_check";
740
741 test_disk_cache_create(mem_ctx, CACHE_DIR_NAME_SF, driver_id);
742
743 /* We skip testing cache size limit as the single file cache currently
744 * doesn't have any functionality to enforce cache size limits.
745 */
746 test_put_and_get(false, driver_id);
747
748 test_put_key_and_get_key(driver_id);
749
750 test_put_and_get_between_instances(driver_id);
751
752 setenv("MESA_DISK_CACHE_SINGLE_FILE", "false", 1);
753
754 int err = rmrf_local(CACHE_TEST_TMP);
755 EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again";
756
757 if (compress) {
758 compress = false;
759 goto run_tests;
760 }
761 #endif
762 }
763
TEST_F(Cache,Database)764 TEST_F(Cache, Database)
765 {
766 const char *driver_id = "make_check_uncompressed";
767
768 #ifndef ENABLE_SHADER_CACHE
769 GTEST_SKIP() << "ENABLE_SHADER_CACHE not defined.";
770 #else
771 setenv("MESA_DISK_CACHE_DATABASE_NUM_PARTS", "1", 1);
772 setenv("MESA_DISK_CACHE_DATABASE", "true", 1);
773
774 test_disk_cache_create(mem_ctx, CACHE_DIR_NAME_DB, driver_id);
775
776 /* We skip testing cache size limit as the single file cache compresses
777 * data much better than the multi-file cache, which results in the
778 * failing tests of the cache eviction function. We we will test the
779 * eviction separately with the disabled compression.
780 */
781 test_put_and_get(false, driver_id);
782
783 int err = rmrf_local(CACHE_TEST_TMP);
784 EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again";
785
786 test_disk_cache_create(mem_ctx, CACHE_DIR_NAME_DB, driver_id);
787
788 test_put_and_get(true, driver_id);
789
790 test_put_key_and_get_key(driver_id);
791
792 test_put_and_get_between_instances(driver_id);
793
794 test_put_and_get_between_instances_with_eviction(driver_id);
795
796 setenv("MESA_DISK_CACHE_DATABASE", "false", 1);
797 unsetenv("MESA_DISK_CACHE_DATABASE_NUM_PARTS");
798
799 err = rmrf_local(CACHE_TEST_TMP);
800 EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again";
801 #endif
802 }
803
TEST_F(Cache,Combined)804 TEST_F(Cache, Combined)
805 {
806 const char *driver_id = "make_check";
807 char blob[] = "This is a RO blob";
808 char blob2[] = "This is a RW blob";
809 uint8_t dummy_key[20] = { 0 };
810 uint8_t blob_key[20];
811 uint8_t blob_key2[20];
812 char foz_rw_idx_file[1024];
813 char foz_ro_idx_file[1024];
814 char foz_rw_file[1024];
815 char foz_ro_file[1024];
816 char *result;
817 size_t size;
818
819 #ifndef ENABLE_SHADER_CACHE
820 GTEST_SKIP() << "ENABLE_SHADER_CACHE not defined.";
821 #else
822 setenv("MESA_DISK_CACHE_SINGLE_FILE", "true", 1);
823 setenv("MESA_DISK_CACHE_DATABASE", "false", 1);
824
825 #ifdef SHADER_CACHE_DISABLE_BY_DEFAULT
826 setenv("MESA_SHADER_CACHE_DISABLE", "false", 1);
827 #endif /* SHADER_CACHE_DISABLE_BY_DEFAULT */
828
829 /* Enable Fossilize read-write cache. */
830 setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "true", 1);
831
832 test_disk_cache_create(mem_ctx, CACHE_DIR_NAME_SF, driver_id);
833
834 /* Create Fossilize writable cache. */
835 struct disk_cache *cache_sf_wr = disk_cache_create("combined_test",
836 driver_id, 0);
837
838 disk_cache_compute_key(cache_sf_wr, blob, sizeof(blob), blob_key);
839 disk_cache_compute_key(cache_sf_wr, blob2, sizeof(blob2), blob_key2);
840
841 /* Ensure that disk_cache_get returns nothing before anything is added. */
842 result = (char *) disk_cache_get(cache_sf_wr, blob_key, &size);
843 EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
844 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
845
846 /* Put blob entry to the cache. */
847 disk_cache_put(cache_sf_wr, blob_key, blob, sizeof(blob), NULL);
848 disk_cache_wait_for_idle(cache_sf_wr);
849
850 result = (char *) disk_cache_get(cache_sf_wr, blob_key, &size);
851 EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
852 EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
853 free(result);
854
855 /* Rename file foz_cache.foz -> ro_cache.foz */
856 sprintf(foz_rw_file, "%s/foz_cache.foz", cache_sf_wr->path);
857 sprintf(foz_ro_file, "%s/ro_cache.foz", cache_sf_wr->path);
858 EXPECT_EQ(rename(foz_rw_file, foz_ro_file), 0) << "foz_cache.foz renaming failed";
859
860 /* Rename file foz_cache_idx.foz -> ro_cache_idx.foz */
861 sprintf(foz_rw_idx_file, "%s/foz_cache_idx.foz", cache_sf_wr->path);
862 sprintf(foz_ro_idx_file, "%s/ro_cache_idx.foz", cache_sf_wr->path);
863 EXPECT_EQ(rename(foz_rw_idx_file, foz_ro_idx_file), 0) << "foz_cache_idx.foz renaming failed";
864
865 disk_cache_destroy(cache_sf_wr);
866
867 /* Disable Fossilize read-write cache. */
868 setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "false", 1);
869
870 /* Set up Fossilize read-only cache. */
871 setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "true", 1);
872 setenv("MESA_DISK_CACHE_READ_ONLY_FOZ_DBS", "ro_cache", 1);
873
874 /* Create FOZ cache that fetches the RO cache. Note that this produces
875 * empty RW cache files. */
876 struct disk_cache *cache_sf_ro = disk_cache_create("combined_test",
877 driver_id, 0);
878
879 /* Blob entry must present because it shall be retrieved from the
880 * ro_cache.foz */
881 result = (char *) disk_cache_get(cache_sf_ro, blob_key, &size);
882 EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
883 EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
884 free(result);
885
886 disk_cache_destroy(cache_sf_ro);
887
888 /* Remove empty FOZ RW cache files created above. We only need RO cache. */
889 EXPECT_EQ(unlink(foz_rw_file), 0);
890 EXPECT_EQ(unlink(foz_rw_idx_file), 0);
891
892 setenv("MESA_DISK_CACHE_SINGLE_FILE", "false", 1);
893 setenv("MESA_DISK_CACHE_DATABASE", "true", 1);
894
895 /* Create MESA-DB cache with enabled retrieval from the read-only
896 * cache. */
897 struct disk_cache *cache_mesa_db = disk_cache_create("combined_test",
898 driver_id, 0);
899
900 /* Dummy entry must not present in any of the caches. Foz cache
901 * reloads index if cache entry is missing. This is a sanity-check
902 * for foz_read_entry(), it should work properly with a disabled
903 * FOZ RW cache. */
904 result = (char *) disk_cache_get(cache_mesa_db, dummy_key, &size);
905 EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
906 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
907
908 /* Blob entry must present because it shall be retrieved from the
909 * read-only cache. */
910 result = (char *) disk_cache_get(cache_mesa_db, blob_key, &size);
911 EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
912 EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
913 free(result);
914
915 /* Blob2 entry must not present in any of the caches. */
916 result = (char *) disk_cache_get(cache_mesa_db, blob_key2, &size);
917 EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
918 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
919
920 /* Put blob2 entry to the cache. */
921 disk_cache_put(cache_mesa_db, blob_key2, blob2, sizeof(blob2), NULL);
922 disk_cache_wait_for_idle(cache_mesa_db);
923
924 /* Blob2 entry must present because it shall be retrieved from the
925 * read-write cache. */
926 result = (char *) disk_cache_get(cache_mesa_db, blob_key2, &size);
927 EXPECT_STREQ(blob2, result) << "disk_cache_get of existing item (pointer)";
928 EXPECT_EQ(size, sizeof(blob2)) << "disk_cache_get of existing item (size)";
929 free(result);
930
931 disk_cache_destroy(cache_mesa_db);
932
933 /* Disable read-only cache. */
934 setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "false", 1);
935
936 /* Create MESA-DB cache with disabled retrieval from the
937 * read-only cache. */
938 cache_mesa_db = disk_cache_create("combined_test", driver_id, 0);
939
940 /* Blob2 entry must present because it shall be retrieved from the
941 * MESA-DB cache. */
942 result = (char *) disk_cache_get(cache_mesa_db, blob_key2, &size);
943 EXPECT_STREQ(blob2, result) << "disk_cache_get of existing item (pointer)";
944 EXPECT_EQ(size, sizeof(blob2)) << "disk_cache_get of existing item (size)";
945 free(result);
946
947 disk_cache_destroy(cache_mesa_db);
948
949 /* Create MESA-DB cache with disabled retrieval from the read-only
950 * cache. */
951 cache_mesa_db = disk_cache_create("combined_test", driver_id, 0);
952
953 /* Blob entry must not present in the cache because we disable the
954 * read-only cache. */
955 result = (char *) disk_cache_get(cache_mesa_db, blob_key, &size);
956 EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
957 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
958
959 disk_cache_destroy(cache_mesa_db);
960
961 /* Create default multi-file cache. */
962 setenv("MESA_DISK_CACHE_DATABASE", "false", 1);
963
964 /* Enable read-only cache. */
965 setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "true", 1);
966
967 /* Create multi-file cache with enabled retrieval from the
968 * read-only cache. */
969 struct disk_cache *cache_multifile = disk_cache_create("combined_test",
970 driver_id, 0);
971
972 /* Blob entry must present because it shall be retrieved from the
973 * read-only cache. */
974 result = (char *) disk_cache_get(cache_multifile, blob_key, &size);
975 EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
976 EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
977 free(result);
978
979 /* Blob2 entry must not present in any of the caches. */
980 result = (char *) disk_cache_get(cache_multifile, blob_key2, &size);
981 EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
982 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
983
984 /* Put blob2 entry to the cache. */
985 disk_cache_put(cache_multifile, blob_key2, blob2, sizeof(blob2), NULL);
986 disk_cache_wait_for_idle(cache_multifile);
987
988 /* Blob2 entry must present because it shall be retrieved from the
989 * read-write cache. */
990 result = (char *) disk_cache_get(cache_multifile, blob_key2, &size);
991 EXPECT_STREQ(blob2, result) << "disk_cache_get of existing item (pointer)";
992 EXPECT_EQ(size, sizeof(blob2)) << "disk_cache_get of existing item (size)";
993 free(result);
994
995 disk_cache_destroy(cache_multifile);
996
997 /* Disable read-only cache. */
998 setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "false", 1);
999 unsetenv("MESA_DISK_CACHE_READ_ONLY_FOZ_DBS");
1000
1001 /* Create multi-file cache with disabled retrieval from the
1002 * read-only cache. */
1003 cache_multifile = disk_cache_create("combined_test", driver_id, 0);
1004
1005 /* Blob entry must not present in the cache because we disabled the
1006 * read-only cache. */
1007 result = (char *) disk_cache_get(cache_multifile, blob_key, &size);
1008 EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
1009 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
1010
1011 /* Blob2 entry must present because it shall be retrieved from the
1012 * read-write cache. */
1013 result = (char *) disk_cache_get(cache_multifile, blob_key2, &size);
1014 EXPECT_STREQ(blob2, result) << "disk_cache_get of existing item (pointer)";
1015 EXPECT_EQ(size, sizeof(blob2)) << "disk_cache_get of existing item (size)";
1016 free(result);
1017
1018 disk_cache_destroy(cache_multifile);
1019
1020 int err = rmrf_local(CACHE_TEST_TMP);
1021 EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again";
1022 #endif
1023 }
1024
TEST_F(Cache,DISABLED_List)1025 TEST_F(Cache, DISABLED_List)
1026 {
1027 const char *driver_id = "make_check";
1028 char blob[] = "This is a RO blob";
1029 uint8_t blob_key[20];
1030 char foz_rw_idx_file[1024];
1031 char foz_ro_idx_file[1024];
1032 char foz_rw_file[1024];
1033 char foz_ro_file[1024];
1034 char *result;
1035 size_t size;
1036
1037 #ifndef ENABLE_SHADER_CACHE
1038 GTEST_SKIP() << "ENABLE_SHADER_CACHE not defined.";
1039 #else
1040 #ifndef FOZ_DB_UTIL_DYNAMIC_LIST
1041 GTEST_SKIP() << "FOZ_DB_UTIL_DYNAMIC_LIST not supported";
1042 #else
1043 setenv("MESA_DISK_CACHE_SINGLE_FILE", "true", 1);
1044
1045 #ifdef SHADER_CACHE_DISABLE_BY_DEFAULT
1046 setenv("MESA_SHADER_CACHE_DISABLE", "false", 1);
1047 #endif /* SHADER_CACHE_DISABLE_BY_DEFAULT */
1048
1049 test_disk_cache_create(mem_ctx, CACHE_DIR_NAME_SF, driver_id);
1050
1051 /* Create ro files for testing */
1052 /* Create Fossilize writable cache. */
1053 struct disk_cache *cache_sf_wr =
1054 disk_cache_create("list_test", driver_id, 0);
1055
1056 disk_cache_compute_key(cache_sf_wr, blob, sizeof(blob), blob_key);
1057
1058 /* Ensure that disk_cache_get returns nothing before anything is added. */
1059 result = (char *)disk_cache_get(cache_sf_wr, blob_key, &size);
1060 EXPECT_EQ(result, nullptr)
1061 << "disk_cache_get with non-existent item (pointer)";
1062 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
1063
1064 /* Put blob entry to the cache. */
1065 disk_cache_put(cache_sf_wr, blob_key, blob, sizeof(blob), NULL);
1066 disk_cache_wait_for_idle(cache_sf_wr);
1067
1068 result = (char *)disk_cache_get(cache_sf_wr, blob_key, &size);
1069 EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
1070 EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
1071 free(result);
1072
1073 /* Rename file foz_cache.foz -> ro_cache.foz */
1074 sprintf(foz_rw_file, "%s/foz_cache.foz", cache_sf_wr->path);
1075 sprintf(foz_ro_file, "%s/ro_cache.foz", cache_sf_wr->path);
1076 EXPECT_EQ(rename(foz_rw_file, foz_ro_file), 0)
1077 << "foz_cache.foz renaming failed";
1078
1079 /* Rename file foz_cache_idx.foz -> ro_cache_idx.foz */
1080 sprintf(foz_rw_idx_file, "%s/foz_cache_idx.foz", cache_sf_wr->path);
1081 sprintf(foz_ro_idx_file, "%s/ro_cache_idx.foz", cache_sf_wr->path);
1082 EXPECT_EQ(rename(foz_rw_idx_file, foz_ro_idx_file), 0)
1083 << "foz_cache_idx.foz renaming failed";
1084
1085 disk_cache_destroy(cache_sf_wr);
1086
1087 const char *list_filename = CACHE_TEST_TMP "/foz_dbs_list.txt";
1088 setenv("MESA_DISK_CACHE_READ_ONLY_FOZ_DBS_DYNAMIC_LIST", list_filename, 1);
1089
1090 /* Create new empty file */
1091 FILE *list_file = fopen(list_filename, "w");
1092 fputs("ro_cache\n", list_file);
1093 fclose(list_file);
1094
1095 /* Create Fossilize writable cache. */
1096 struct disk_cache *cache_sf = disk_cache_create("list_test", driver_id, 0);
1097
1098 /* Blob entry must present because it shall be retrieved from the
1099 * ro_cache.foz loaded from list at creation time */
1100 result = (char *)disk_cache_get(cache_sf, blob_key, &size);
1101 EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
1102 EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
1103 free(result);
1104
1105 disk_cache_destroy(cache_sf);
1106 remove(list_filename);
1107
1108 /* Test loading from a list populated at runtime */
1109 /* Create new empty file */
1110 list_file = fopen(list_filename, "w");
1111 fclose(list_file);
1112
1113 /* Create Fossilize writable cache. */
1114 cache_sf = disk_cache_create("list_test", driver_id, 0);
1115
1116 /* Ensure that disk_cache returns nothing before list file is populated */
1117 result = (char *)disk_cache_get(cache_sf, blob_key, &size);
1118 EXPECT_EQ(result, nullptr)
1119 << "disk_cache_get with non-existent item (pointer)";
1120 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
1121
1122 /* Add ro_cache to list file for loading */
1123 list_file = fopen(list_filename, "a");
1124 fputs("ro_cache\n", list_file);
1125 fclose(list_file);
1126
1127 /* Poll result to give time for updater to load ro cache */
1128 result = (char *)poll_disk_cache_get(cache_sf, blob_key, &size);
1129
1130 /* Blob entry must present because it shall be retrieved from the
1131 * ro_cache.foz loaded from list at runtime */
1132 EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
1133 EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
1134 free(result);
1135
1136 disk_cache_destroy(cache_sf);
1137 remove(list_filename);
1138
1139 /* Test loading from a list with some invalid files */
1140 /* Create new empty file */
1141 list_file = fopen(list_filename, "w");
1142 fclose(list_file);
1143
1144 /* Create Fossilize writable cache. */
1145 cache_sf = disk_cache_create("list_test", driver_id, 0);
1146
1147 /* Ensure that disk_cache returns nothing before list file is populated */
1148 result = (char *)disk_cache_get(cache_sf, blob_key, &size);
1149 EXPECT_EQ(result, nullptr)
1150 << "disk_cache_get with non-existent item (pointer)";
1151 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
1152
1153 /* Add non-existant list files for loading */
1154 list_file = fopen(list_filename, "a");
1155 fputs("no_cache\n", list_file);
1156 fputs("no_cache2\n", list_file);
1157 fputs("no_cache/no_cache3\n", list_file);
1158 /* Add ro_cache to list file for loading */
1159 fputs("ro_cache\n", list_file);
1160 fclose(list_file);
1161
1162 /* Poll result to give time for updater to load ro cache */
1163 result = (char *)poll_disk_cache_get(cache_sf, blob_key, &size);
1164
1165 /* Blob entry must present because it shall be retrieved from the
1166 * ro_cache.foz loaded from list at runtime despite invalid files
1167 * in the list */
1168 EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
1169 EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
1170 free(result);
1171
1172 disk_cache_destroy(cache_sf);
1173 remove(list_filename);
1174
1175 int err = rmrf_local(CACHE_TEST_TMP);
1176 EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again";
1177
1178 unsetenv("MESA_DISK_CACHE_SINGLE_FILE");
1179 unsetenv("MESA_DISK_CACHE_READ_ONLY_FOZ_DBS_DYNAMIC_LIST");
1180 #endif /* FOZ_DB_UTIL_DYNAMIC_LIST */
1181 #endif /* ENABLE_SHADER_CACHE */
1182 }
1183
1184 static void
test_multipart_eviction(const char * driver_id)1185 test_multipart_eviction(const char *driver_id)
1186 {
1187 const unsigned int entry_size = 512;
1188 uint8_t blobs[7][entry_size];
1189 cache_key keys[7];
1190 unsigned int i;
1191 char *result;
1192 size_t size;
1193
1194 setenv("MESA_SHADER_CACHE_MAX_SIZE", "3K", 1);
1195 setenv("MESA_DISK_CACHE_DATABASE_EVICTION_SCORE_2X_PERIOD", "1", 1);
1196
1197 struct disk_cache *cache = disk_cache_create("test", driver_id, 0);
1198
1199 unsigned int entry_file_size = entry_size;
1200 entry_file_size -= sizeof(struct cache_entry_file_data);
1201 entry_file_size -= mesa_cache_db_file_entry_size();
1202 entry_file_size -= cache->driver_keys_blob_size;
1203 entry_file_size -= 4 + 8; /* cache_item_metadata size + room for alignment */
1204
1205 /*
1206 * 1. Allocate 3KB cache in 3 parts, each part is 1KB
1207 * 2. Fill up cache with six 512K entries
1208 * 3. Touch entries of the first part, which will bump last_access_time
1209 * of the first two cache entries
1210 * 4. Insert seventh 512K entry that will cause eviction of the second part
1211 * 5. Check that second entry of the second part gone due to eviction and
1212 * others present
1213 */
1214
1215 /* Fill up cache with six 512K entries. */
1216 for (i = 0; i < 6; i++) {
1217 memset(blobs[i], i, entry_file_size);
1218
1219 disk_cache_compute_key(cache, blobs[i], entry_file_size, keys[i]);
1220 disk_cache_put(cache, keys[i], blobs[i], entry_file_size, NULL);
1221 disk_cache_wait_for_idle(cache);
1222
1223 result = (char *) disk_cache_get(cache, keys[i], &size);
1224 EXPECT_NE(result, nullptr) << "disk_cache_get with existent item (pointer)";
1225 EXPECT_EQ(size, entry_file_size) << "disk_cache_get with existent item (size)";
1226 free(result);
1227
1228 /* Ensure that cache entries will have distinct last_access_time
1229 * during testing.
1230 */
1231 if (i % 2 == 0)
1232 usleep(100000);
1233 }
1234
1235 /* Touch entries of the first part. Second part becomes outdated */
1236 for (i = 0; i < 2; i++) {
1237 result = (char *) disk_cache_get(cache, keys[i], &size);
1238 EXPECT_NE(result, nullptr) << "disk_cache_get with existent item (pointer)";
1239 EXPECT_EQ(size, entry_file_size) << "disk_cache_get with existent item (size)";
1240 free(result);
1241 }
1242
1243 /* Insert seventh entry. */
1244 memset(blobs[6], 6, entry_file_size);
1245 disk_cache_compute_key(cache, blobs[6], entry_file_size, keys[6]);
1246 disk_cache_put(cache, keys[6], blobs[6], entry_file_size, NULL);
1247 disk_cache_wait_for_idle(cache);
1248
1249 /* Check whether third entry of the second part gone and others present. */
1250 for (i = 0; i < ARRAY_SIZE(blobs); i++) {
1251 result = (char *) disk_cache_get(cache, keys[i], &size);
1252 if (i == 2) {
1253 EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
1254 } else {
1255 EXPECT_NE(result, nullptr) << "disk_cache_get with existent item (pointer)";
1256 EXPECT_EQ(size, entry_file_size) << "disk_cache_get with existent item (size)";
1257 }
1258 free(result);
1259 }
1260
1261 disk_cache_destroy(cache);
1262 }
1263
TEST_F(Cache,DatabaseMultipartEviction)1264 TEST_F(Cache, DatabaseMultipartEviction)
1265 {
1266 const char *driver_id = "make_check_uncompressed";
1267
1268 #ifndef ENABLE_SHADER_CACHE
1269 GTEST_SKIP() << "ENABLE_SHADER_CACHE not defined.";
1270 #else
1271 setenv("MESA_DISK_CACHE_DATABASE_NUM_PARTS", "3", 1);
1272 setenv("MESA_DISK_CACHE_DATABASE", "true", 1);
1273
1274 test_disk_cache_create(mem_ctx, CACHE_DIR_NAME_DB, driver_id);
1275
1276 test_multipart_eviction(driver_id);
1277
1278 unsetenv("MESA_DISK_CACHE_DATABASE_NUM_PARTS");
1279 unsetenv("MESA_DISK_CACHE_DATABASE");
1280
1281 int err = rmrf_local(CACHE_TEST_TMP);
1282 EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again";
1283 #endif
1284 }
1285
1286 static void
test_put_and_get_disabled(const char * driver_id)1287 test_put_and_get_disabled(const char *driver_id)
1288 {
1289 struct disk_cache *cache;
1290 char blob[] = "This is a blob of thirty-seven bytes";
1291 uint8_t blob_key[20];
1292 char *result;
1293 size_t size;
1294
1295 cache = disk_cache_create("test", driver_id, 0);
1296
1297 disk_cache_compute_key(cache, blob, sizeof(blob), blob_key);
1298
1299 /* Ensure that disk_cache_get returns nothing before anything is added. */
1300 result = (char *) disk_cache_get(cache, blob_key, &size);
1301 EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
1302 EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
1303
1304 /* Simple test of put and get. */
1305 disk_cache_put(cache, blob_key, blob, sizeof(blob), NULL);
1306
1307 /* disk_cache_put() hands things off to a thread so wait for it. */
1308 disk_cache_wait_for_idle(cache);
1309
1310 result = (char *) disk_cache_get(cache, blob_key, &size);
1311 EXPECT_STREQ(result, nullptr) << "disk_cache_get of existing item (pointer)";
1312 EXPECT_EQ(size, 0) << "disk_cache_get of existing item (size)";
1313
1314 disk_cache_destroy(cache);
1315 }
1316
TEST_F(Cache,Disabled)1317 TEST_F(Cache, Disabled)
1318 {
1319 const char *driver_id = "make_check";
1320
1321 #ifndef ENABLE_SHADER_CACHE
1322 GTEST_SKIP() << "ENABLE_SHADER_CACHE not defined.";
1323 #else
1324 setenv("MESA_DISK_CACHE_SINGLE_FILE", "true", 1);
1325
1326 #ifdef SHADER_CACHE_DISABLE_BY_DEFAULT
1327 setenv("MESA_SHADER_CACHE_DISABLE", "false", 1);
1328 #endif /* SHADER_CACHE_DISABLE_BY_DEFAULT */
1329
1330 test_disk_cache_create(mem_ctx, CACHE_DIR_NAME_SF, driver_id);
1331
1332 test_put_and_get(false, driver_id);
1333
1334 setenv("MESA_SHADER_CACHE_DISABLE", "true", 1);
1335
1336 test_put_and_get_disabled(driver_id);
1337
1338 setenv("MESA_SHADER_CACHE_DISABLE", "false", 1);
1339 setenv("MESA_DISK_CACHE_SINGLE_FILE", "false", 1);
1340
1341 int err = rmrf_local(CACHE_TEST_TMP);
1342 EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again";
1343 #endif
1344 }
1345