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