1 //
2 // Copyright 2020 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // shader_cache_file_hooking:
7 // Hooks file API functions to intercept Metal shader cache access and return as if the file
8 // doesn't exist. This is to avoid slow cache access happened after the cache becomes huge over
9 // time.
10 //
11
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <iostream>
18 #include <regex>
19
20 namespace
21 {
22 constexpr char kMetalCacheExpr[] = R"(.*com\.apple\.metal.*(libraries|functions).*\.(maps|data))";
23 }
24
HookedOpen(const char * path,int flags,mode_t mode)25 int HookedOpen(const char *path, int flags, mode_t mode)
26 {
27 std::regex expr(kMetalCacheExpr);
28
29 if (!std::regex_match(path, expr))
30 {
31 return open(path, flags, mode);
32 }
33
34 #if !defined(NDEBUG)
35 static int messageRepeatCount = 5;
36 if (messageRepeatCount > 0)
37 {
38 messageRepeatCount--;
39 std::cerr << "open(\"" << path << "\", \"" << flags
40 << "\") is skipped. This message will repeat " << messageRepeatCount
41 << " more times." << std::endl;
42 }
43 #endif
44
45 if (flags & O_CREAT)
46 {
47 errno = EACCES;
48 return -1;
49 }
50
51 // Treat as if the cache doesn't exist
52 errno = ENOENT;
53 return -1;
54 }
55
HookedFopen(const char * filename,const char * mode)56 FILE *HookedFopen(const char *filename, const char *mode)
57 {
58 std::regex expr(kMetalCacheExpr);
59 if (!std::regex_match(filename, expr))
60 {
61 return fopen(filename, mode);
62 }
63
64 #if !defined(NDEBUG)
65 static int messageRepeatCount = 5;
66 if (messageRepeatCount > 0)
67 {
68 messageRepeatCount--;
69 std::cerr << "fopen(\"" << filename << "\", \"" << mode
70 << "\") is skipped. This message will repeat " << messageRepeatCount
71 << " more times." << std::endl;
72 }
73 #endif
74
75 if (strstr(mode, "r"))
76 {
77 // Treat as if the cache doesn't exist
78 errno = ENOENT;
79 return nullptr;
80 }
81
82 errno = EACCES;
83 return nullptr;
84 }
85
86 // See https://opensource.apple.com/source/dyld/dyld-210.2.3/include/mach-o/dyld-interposing.h
87 #define DYLD_INTERPOSE(_replacment, _replacee) \
88 __attribute__((used)) static struct \
89 { \
90 const void *replacment; \
91 const void *replacee; \
92 } _interpose_##_replacee __attribute__((section("__DATA,__interpose"))) = { \
93 (const void *)(unsigned long)&_replacment, (const void *)(unsigned long)&_replacee};
94
95 DYLD_INTERPOSE(HookedOpen, open)
96 DYLD_INTERPOSE(HookedFopen, fopen)
97