1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.. All rights reserved.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "egl_blob_cache.h"
16 #include "wrapper_log.h"
17 #include <sys/stat.h>
18 #include <sys/mman.h>
19 #include <errors.h>
20 #include <cinttypes>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <thread>
24 
25 namespace OHOS {
26 static const char* CACHE_MAGIC = "OSOH";
27 BlobCache* BlobCache::blobCache_;
28 
Get()29 BlobCache* BlobCache::Get()
30 {
31     if (BlobCache::blobCache_ == nullptr) {
32         BlobCache::blobCache_ = new BlobCache();
33     }
34     return BlobCache::blobCache_;
35 }
36 
Blob(const void * dataIn,size_t size)37 BlobCache::Blob::Blob(const void *dataIn, size_t size) :dataSize(size)
38 {
39     prev_ = nullptr;
40     next_ = nullptr;
41     data = nullptr;
42     if (size > 0) {
43         data = malloc(size);
44     }
45     if (data == nullptr) {
46         dataSize = 0;
47     }
48     if (data != nullptr) {
49         memcpy_s(data, size, const_cast<void *>(dataIn), size);
50     }
51 }
52 
~Blob()53 BlobCache::Blob::~Blob()
54 {
55     free(data);
56 }
57 
BlobCache()58 BlobCache::BlobCache()
59 {
60     head_ = std::make_shared<Blob>(nullptr, 0);
61     tail_ = std::make_shared<Blob>(nullptr, 0);
62     head_->next_ = tail_;
63     tail_->prev_ = head_;
64     blobSizeMax_ = MAX_SHADER;
65     maxShaderSize_ = MAX_SHADER_SIZE;
66     initStatus_ = false;
67     fileName_ = std::string("/blobShader");
68     saveStatus_ = false;
69     blobSize_ = 0;
70 }
71 
~BlobCache()72 BlobCache::~BlobCache()
73 {
74     if (blobCache_) {
75         blobCache_ = nullptr;
76     }
77 }
78 
Terminate()79 void BlobCache::Terminate()
80 {
81     if (blobCache_) {
82         blobCache_->WriteToDisk();
83     }
84     blobCache_ = nullptr;
85 }
86 
GetMapSize() const87 int BlobCache::GetMapSize() const
88 {
89     if (blobCache_) {
90         return (int)mBlobMap_.size();
91     }
92     return 0;
93 }
94 
Init(EglWrapperDisplay * display)95 void BlobCache::Init(EglWrapperDisplay* display)
96 {
97     if (!initStatus_) {
98         return;
99     }
100     EglWrapperDispatchTablePtr table = &gWrapperHook;
101     if (table->isLoad && table->egl.eglSetBlobCacheFuncsANDROID) {
102         table->egl.eglSetBlobCacheFuncsANDROID(display->GetEglDisplay(),
103                                                BlobCache::SetBlobFunc, BlobCache::GetBlobFunc);
104     } else {
105         WLOGE("eglSetBlobCacheFuncsANDROID not found.");
106     }
107 }
108 
SetBlobFunc(const void * key,EGLsizeiANDROID keySize,const void * value,EGLsizeiANDROID valueSize)109 void BlobCache::SetBlobFunc(const void* key, EGLsizeiANDROID keySize, const void* value,
110                             EGLsizeiANDROID valueSize)
111 {
112     BlobCache::Get()->SetBlobLock(key, keySize, value, valueSize);
113 }
GetBlobFunc(const void * key,EGLsizeiANDROID keySize,void * value,EGLsizeiANDROID valueSize)114 EGLsizeiANDROID BlobCache::GetBlobFunc(const void *key, EGLsizeiANDROID keySize, void *value,
115                                        EGLsizeiANDROID valueSize)
116 {
117     return BlobCache::Get()->GetBlobLock(key, keySize, value, valueSize);
118 }
119 
SetBlobLock(const void * key,EGLsizeiANDROID keySize,const void * value,EGLsizeiANDROID valueSize)120 void BlobCache::SetBlobLock(const void* key, EGLsizeiANDROID keySize, const void* value,
121                             EGLsizeiANDROID valueSize)
122 {
123     std::lock_guard<std::mutex> lock(blobmutex_);
124     SetBlob(key, keySize, value, valueSize);
125 }
GetBlobLock(const void * key,EGLsizeiANDROID keySize,void * value,EGLsizeiANDROID valueSize)126 EGLsizeiANDROID BlobCache::GetBlobLock(const void *key, EGLsizeiANDROID keySize, void *value,
127                                        EGLsizeiANDROID valueSize)
128 {
129     std::lock_guard<std::mutex> lock(blobmutex_);
130     return GetBlob(key, keySize, value, valueSize);
131 }
132 
MoveToFront(std::shared_ptr<Blob> & cur)133 void BlobCache::MoveToFront(std::shared_ptr<Blob>& cur)
134 {
135     head_->next_->prev_ = cur;
136     cur->next_ = head_->next_;
137     head_->next_ = cur;
138     cur->prev_ = head_;
139 }
140 
SetBlob(const void * key,EGLsizeiANDROID keySize,const void * value,EGLsizeiANDROID valueSize)141 void BlobCache::SetBlob(const void *key, EGLsizeiANDROID keySize, const void *value,
142                         EGLsizeiANDROID valueSize)
143 {
144     if (keySize <= 0 || valueSize <= 0 || key == nullptr || value == nullptr) {
145         return;
146     }
147     if (!readStatus_) {
148         ReadFromDisk();
149     }
150     std::shared_ptr<Blob> keyBlob = std::make_shared<Blob>(key, (size_t)keySize);
151     auto it = mBlobMap_.find(keyBlob);
152     if (it != mBlobMap_.end()) {
153         free(it->second->data);
154         it->second->data = malloc(valueSize);
155         if (it->second->data != nullptr) {
156             it->second->dataSize = valueSize;
157         } else {
158             it->second->dataSize = 0;
159             return;
160         }
161         memcpy_s(it->second->data, valueSize, value, valueSize);
162         return;
163     }
164     if (blobSize_ >= blobSizeMax_) {
165         int count = 0;
166         while (count <= MAX_SHADER_DELETE) {
167             std::shared_ptr<Blob> deleteblob = tail_->prev_;
168             deleteblob->prev_->next_ = tail_;
169             tail_->prev_ = deleteblob->prev_;
170             mBlobMap_.erase(deleteblob);
171             --blobSize_;
172             ++count;
173         }
174     }
175     std::shared_ptr<Blob> valueBlob = std::make_shared<Blob>(value, (size_t)valueSize);
176     mBlobMap_.emplace(keyBlob, valueBlob);
177     MoveToFront(keyBlob);
178     ++blobSize_;
179     if (!saveStatus_) {
180         saveStatus_ = true;
181         std::thread deferSaveThread([this]() {
182             sleep(DEFER_SAVE_MIN);
183             if (blobCache_) {
184                 blobCache_->WriteToDisk();
185             }
186             saveStatus_ = false;
187         });
188         deferSaveThread.detach();
189     }
190 }
191 
GetBlob(const void * key,EGLsizeiANDROID keySize,void * value,EGLsizeiANDROID valueSize)192 EGLsizeiANDROID BlobCache::GetBlob(const void *key, EGLsizeiANDROID keySize, void *value,
193                                    EGLsizeiANDROID valueSize)
194 {
195     EGLsizeiANDROID ret = 0;
196     if (keySize <= 0 || key == nullptr) {
197         return ret;
198     }
199     if (!readStatus_) {
200         ReadFromDisk();
201     }
202     std::shared_ptr<Blob> keyBlob = std::make_shared<Blob>(key, (size_t)keySize);
203     auto it = mBlobMap_.find(keyBlob);
204     if (it != mBlobMap_.end()) {
205         ret = static_cast<EGLsizeiANDROID>(it->second->dataSize);
206         if (valueSize < ret) {
207             WLOGD("valueSize not enough");
208         } else if (ret == 0) {
209             WLOGE("shader not exist");
210         } else {
211             errno_t status = memcpy_s(value, valueSize, it->second->data, it->second->dataSize);
212             if (status != EOK) {
213                 WLOGE("memcpy_s failed");
214                 return ret;
215             }
216             auto moveblob = it->first;
217             moveblob->prev_->next_ = moveblob->next_;
218             moveblob->next_->prev_ = moveblob->prev_;
219             MoveToFront(moveblob);
220         }
221     }
222 
223     return ret;
224 }
225 
SetCacheDir(const std::string dir)226 void BlobCache::SetCacheDir(const std::string dir)
227 {
228     struct stat dirStat;
229     if (stat(dir.c_str(), &dirStat) != 0) {
230         WLOGE("inputdir not Create");
231         initStatus_ = false;
232         return;
233     }
234 
235     struct stat cachedirstat;
236     struct stat ddkdirstat;
237     cacheDir_ = dir + std::string("/blobShader");
238     ddkCacheDir_ = dir + std::string("/ddkShader");
239 
240     if (stat(cacheDir_.c_str(), &cachedirstat) != 0) {
241         initStatus_ = false;
242         int ret = mkdir(cacheDir_.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH);
243         if (ret == -1) {
244             initStatus_ = false;
245             WLOGE("cacheDir_ not Create");
246         } else {
247             initStatus_ = true;
248         }
249     } else {
250         initStatus_ = true;
251     }
252 
253     if (stat(ddkCacheDir_.c_str(), &ddkdirstat) != 0) {
254         int ret = mkdir(ddkCacheDir_.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH);
255         if (ret == -1 && errno != EEXIST) {
256             WLOGE("ddkCacheDir_ not Create");
257         }
258     }
259 }
260 
SetCacheShaderSize(int shadermax)261 void BlobCache::SetCacheShaderSize(int shadermax)
262 {
263     if (shadermax <= 0 || shadermax > MAX_SHADER) {
264         return;
265     }
266     blobSizeMax_ = shadermax;
267 }
268 
Formatfile(size_t size)269 static inline size_t Formatfile(size_t size)
270 {
271     return (size + FORMAT_OFFSET) & ~FORMAT_OFFSET;
272 }
273 
GetCacheSize() const274 size_t BlobCache::GetCacheSize() const
275 {
276     size_t headSize = sizeof(CacheHeader);
277     size_t ret = 0;
278     for (auto item = mBlobMap_.begin(); item != mBlobMap_.end(); ++item) {
279         size_t innerSize = headSize + item->first->dataSize + item->second->dataSize;
280         ret += Formatfile(innerSize);
281     }
282     return ret;
283 }
284 
WriteToDisk()285 void BlobCache::WriteToDisk()
286 {
287     std::lock_guard<std::mutex> lock(blobmutex_);
288     struct stat dirStat;
289     if (stat(cacheDir_.c_str(), &dirStat) != 0) {
290         return;
291     }
292     std::string storefile = cacheDir_ + fileName_;
293     int fd = open(storefile.c_str(), O_CREAT | O_EXCL | O_RDWR, 0);
294     if (fd == -1) {
295         if (errno == EEXIST) {
296             if (unlink(storefile.c_str()) == -1) {
297                 return;
298             }
299             fd = open(storefile.c_str(), O_CREAT | O_EXCL | O_RDWR, 0);
300         }
301         if (fd == -1) {
302             return;
303         }
304     }
305     size_t filesize = GetCacheSize();
306     size_t headsize = sizeof(CacheHeader);
307     size_t bufsize = filesize + CACHE_HEAD;
308     uint8_t *buf = new uint8_t[bufsize];
309     size_t offset = CACHE_HEAD;
310     for (auto item = mBlobMap_.begin(); item != mBlobMap_.end(); ++item) {
311         CacheHeader* eheader = reinterpret_cast<CacheHeader*>(&buf[offset]);
312         size_t keysize = item->first->dataSize;
313         size_t valuesize = item->second->dataSize;
314         eheader->keySize = keysize;
315         eheader->valueSize = valuesize;
316         if (memcpy_s(eheader->mData, bufsize - offset - headsize, item->first->data, keysize) != 0) {
317             delete[] buf;
318             close(fd);
319             return;
320         }
321 
322         if (memcpy_s(eheader->mData + keysize, bufsize - offset - headsize - keysize,
323                      item->second->data, valuesize) !=0) {
324             delete[] buf;
325             close(fd);
326             return;
327         }
328         size_t innerSize = headsize + keysize + valuesize;
329         offset += Formatfile(innerSize);
330     }
331     if (memcpy_s(buf, bufsize, CACHE_MAGIC, CACHE_MAGIC_HEAD) != 0) {
332         delete[] buf;
333         close(fd);
334         return;
335     }
336     uint32_t *crc = reinterpret_cast<uint32_t*>(buf + CACHE_MAGIC_HEAD);
337     *crc = CrcGen(buf + CACHE_HEAD, filesize);
338     if (write(fd, buf, bufsize) != -1) {
339         fchmod(fd, S_IRUSR);
340     }
341     delete[] buf;
342     close(fd);
343 }
344 
ReadFromDisk()345 void BlobCache::ReadFromDisk()
346 {
347     readStatus_ = true;
348     std::string storefile = cacheDir_ + fileName_;
349     int fd = open(storefile.c_str(), O_RDONLY, 0);
350     if (fd == -1) {
351         close(fd);
352         return;
353     }
354     struct stat bufstat;
355     if (fstat(fd, &bufstat) == -1) {
356         close(fd);
357         return;
358     }
359     if (bufstat.st_size <= 0 || static_cast<size_t>(bufstat.st_size) > maxShaderSize_ + maxShaderSize_) {
360         close(fd);
361         return;
362     }
363     size_t filesize = static_cast<size_t>(bufstat.st_size);
364     uint8_t *buf = reinterpret_cast<uint8_t*>(mmap(nullptr, filesize, PROT_READ, MAP_PRIVATE, fd, 0));
365     if (buf == MAP_FAILED) {
366         close(fd);
367         return;
368     }
369     if (!ValidFile(buf, filesize)) {
370         munmap(buf, filesize);
371         close(fd);
372         return;
373     }
374     size_t headsize = sizeof(CacheHeader);
375     size_t byteoffset = CACHE_HEAD;
376     while (byteoffset < filesize - CACHE_HEAD) {
377         const CacheHeader* eheader = reinterpret_cast<CacheHeader*>(&buf[byteoffset]);
378         size_t keysize = eheader->keySize;
379         size_t valuesize = eheader->valueSize;
380         if (byteoffset + headsize + keysize > filesize) {
381             break;
382         }
383         const uint8_t *key = eheader->mData;
384         if (byteoffset + headsize + keysize + valuesize > filesize) {
385             break;
386         }
387         const uint8_t *value = eheader->mData + keysize;
388         SetBlob(key, static_cast<EGLsizeiANDROID>(keysize), value, static_cast<EGLsizeiANDROID>(valuesize));
389         size_t innerSize = headsize + keysize + valuesize;
390         byteoffset += Formatfile(innerSize);
391     }
392     munmap(buf, filesize);
393     close(fd);
394 }
395 
396 //CRC standard function
CrcGen(const uint8_t * buf,size_t len)397 uint32_t BlobCache::CrcGen(const uint8_t *buf, size_t len)
398 {
399     const uint32_t polynoimal = 0xEDB88320;
400     uint32_t crc = 0xFFFFFFFF;
401 
402     for (size_t i = 0; i < len ; ++i) {
403         crc ^= (static_cast<uint32_t>(buf[i]) << CRC_NUM);
404         for (size_t j = 0; j < BYTE_SIZE; ++j) {
405             if (crc & 0x80000000) {
406                 crc = (crc << 1) ^ polynoimal;
407             } else {
408                 crc <<= 1;
409             }
410         }
411     }
412     return crc;
413 }
414 
ValidFile(uint8_t * buf,size_t len)415 bool BlobCache::ValidFile(uint8_t *buf, size_t len)
416 {
417     if (memcmp(buf, CACHE_MAGIC, CACHE_MAGIC_HEAD) != 0) {
418         WLOGE("CACHE_MAGIC failed");
419         return false;
420     }
421 
422     uint32_t* crcfile = reinterpret_cast<uint32_t*>(buf + CACHE_MAGIC_HEAD);
423     uint32_t crccur = CrcGen(buf + CACHE_HEAD, len - CACHE_HEAD);
424     if (crccur != *crcfile) {
425         WLOGE("crc check failed");
426         return false;
427     }
428 
429     return true;
430 }
431 
EglSetCacheDir(const std::string dir)432 void EglSetCacheDir(const std::string dir)
433 {
434     BlobCache::Get()->SetCacheDir(dir);
435 }
436 }