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