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