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