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