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