• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
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 
16 #include "nncompiled_cache.h"
17 
18 #include <unistd.h>
19 #include <functional>
20 #include <memory>
21 #include <limits>
22 #include <cstdio>
23 
24 #include "utils.h"
25 #include "backend_manager.h"
26 #include "nnbackend.h"
27 
28 namespace OHOS {
29 namespace NeuralNetworkRuntime {
30 constexpr int32_t NULL_PTR_LENGTH = 0;
31 constexpr int32_t NUMBER_CACHE_INFO_MEMBERS = 3;
32 constexpr int32_t NUMBER_CACHE_INFO_EXTENSION_MEMBERS = 2;
33 constexpr int32_t HEX_UNIT = 16;
34 constexpr char ROOT_DIR_STR = '/';
35 constexpr char DOUBLE_SLASH_STR[] = "//";
36 constexpr int OPVERSION_SUBSTR_NUM = 2;
37 const std::string CURRENT_VERSION = "0x00000000";
38 const std::string HIAI_VERSION_PATH = "/data/data/hiai/version";
39 
Save(const std::vector<OHOS::NeuralNetworkRuntime::Buffer> & caches,const std::string & cacheDir,uint32_t version)40 OH_NN_ReturnCode NNCompiledCache::Save(const std::vector<OHOS::NeuralNetworkRuntime::Buffer>& caches,
41                                        const std::string& cacheDir,
42                                        uint32_t version)
43 {
44     LOGI("[NNCompiledCache::Save] m_isExceedRamLimit: %{public}d", static_cast<int>(m_isExceedRamLimit));
45     if (caches.empty()) {
46         LOGE("[NNCompiledCache] Save failed, caches is empty.");
47         return OH_NN_INVALID_PARAMETER;
48     }
49 
50     if (m_device == nullptr) {
51         LOGE("[NNCompiledCache] Save failed, m_device is empty.");
52         return OH_NN_INVALID_PARAMETER;
53     }
54 
55     OH_NN_ReturnCode ret = GenerateCacheFiles(caches, cacheDir, version);
56     if (ret != OH_NN_SUCCESS) {
57         LOGE("[NNCompiledCache] Save failed, error happened when calling GenerateCacheFiles.");
58         return ret;
59     }
60 
61     LOGI("[NNCompiledCache] Save success. %zu caches are saved.", caches.size());
62     return OH_NN_SUCCESS;
63 }
64 
Restore(const std::string & cacheDir,uint32_t version,std::vector<OHOS::NeuralNetworkRuntime::Buffer> & caches)65 OH_NN_ReturnCode NNCompiledCache::Restore(const std::string& cacheDir,
66                                           uint32_t version,
67                                           std::vector<OHOS::NeuralNetworkRuntime::Buffer>& caches)
68 {
69     if (cacheDir.empty()) {
70         LOGE("[NNCompiledCache] Restore failed, cacheDir is empty.");
71         return OH_NN_INVALID_PARAMETER;
72     }
73 
74     if (!caches.empty()) {
75         LOGE("[NNCompiledCache] Restore failed, caches is not empty.");
76         return OH_NN_INVALID_PARAMETER;
77     }
78 
79     if (m_device == nullptr) {
80         LOGE("[NNCompiledCache] Restore failed, m_device is empty.");
81         return OH_NN_INVALID_PARAMETER;
82     }
83 
84     std::string cacheInfoPath = cacheDir + "/" + m_modelName + "cache_info.nncache";
85     char path[PATH_MAX];
86     if (realpath(cacheInfoPath.c_str(), path) == nullptr) {
87         LOGE("[NNCompiledCache] Restore failed, fail to get the real path of cacheInfoPath.");
88         return OH_NN_INVALID_PARAMETER;
89     }
90     if (access(path, F_OK) != 0) {
91         LOGE("[NNCompiledCache] Restore failed, cacheInfoPath is not exist.");
92         return OH_NN_INVALID_PARAMETER;
93     }
94 
95     NNCompiledCacheInfo cacheInfo;
96     OH_NN_ReturnCode ret = CheckCacheInfo(cacheInfo, path);
97     if (ret != OH_NN_SUCCESS) {
98         LOGE("[NNCompiledCache] Restore failed, error happened when calling CheckCacheInfo.");
99         return ret;
100     }
101 
102     if (static_cast<int64_t>(version) > cacheInfo.version) {
103         LOGE("[NNCompiledCache] Restore failed, version is not match.");
104         return OH_NN_INVALID_PARAMETER;
105     }
106 
107     if (static_cast<int64_t>(version) < cacheInfo.version) {
108         LOGE("[NNCompiledCache] Restore failed, the current version is lower than the cache files, "
109              "please set a higher version.");
110         return OH_NN_OPERATION_FORBIDDEN;
111     }
112 
113     for (uint32_t i = 0; i < cacheInfo.fileNumber; ++i) {
114         std::string cacheModelPath = cacheDir + "/" + m_modelName + std::to_string(i) + ".nncache";
115         OHOS::NeuralNetworkRuntime::Buffer modelBuffer;
116         ret = ReadCacheModelFile(cacheModelPath, modelBuffer);
117         if (ret != OH_NN_SUCCESS) {
118             LOGE("[NNCompiledCache] Restore failed, error happened when calling ReadCacheModelFile.");
119             return ret;
120         }
121 
122         if (GetCrc16(static_cast<char*>(modelBuffer.data), modelBuffer.length) !=
123             cacheInfo.modelCheckSum[i]) {
124             LOGE("[NNCompiledCache] Restore failed, the cache model file %{public}s has been changed.",
125                  cacheModelPath.c_str());
126             return OH_NN_INVALID_FILE;
127         }
128 
129         caches.emplace_back(std::move(modelBuffer));
130     }
131 
132     return ret;
133 }
134 
SetBackend(size_t backendID)135 OH_NN_ReturnCode NNCompiledCache::SetBackend(size_t backendID)
136 {
137     BackendManager& backendManager = BackendManager::GetInstance();
138     std::shared_ptr<Backend> backend = backendManager.GetBackend(backendID);
139     if (backend == nullptr) {
140         LOGE("[NNCompiledCache] SetBackend failed, backend with backendID %{public}zu is not exist.", backendID);
141         return OH_NN_INVALID_PARAMETER;
142     }
143 
144     std::shared_ptr<NNBackend> nnBackend = std::reinterpret_pointer_cast<NNBackend>(backend);
145     m_device = nnBackend->GetDevice();
146     if (m_device == nullptr) {
147         LOGE("[NNCompiledCache] SetBackend failed, device with backendID %{public}zu is not exist.", backendID);
148         return OH_NN_FAILED;
149     }
150 
151     m_backendID = backendID;
152     return OH_NN_SUCCESS;
153 }
154 
SetModelName(const std::string & modelName)155 void NNCompiledCache::SetModelName(const std::string& modelName)
156 {
157     m_modelName = modelName;
158 }
159 
SetIsExceedRamLimit(const bool isExceedRamLimit)160 void NNCompiledCache::SetIsExceedRamLimit(const bool isExceedRamLimit)
161 {
162     m_isExceedRamLimit = isExceedRamLimit;
163 }
164 
GenerateCacheFiles(const std::vector<OHOS::NeuralNetworkRuntime::Buffer> & caches,const std::string & cacheDir,uint32_t version) const165 OH_NN_ReturnCode NNCompiledCache::GenerateCacheFiles(const std::vector<OHOS::NeuralNetworkRuntime::Buffer>& caches,
166                                                      const std::string& cacheDir,
167                                                      uint32_t version) const
168 {
169     const size_t cacheNumber = caches.size();
170     uint32_t cacheSize = NUMBER_CACHE_INFO_MEMBERS + cacheNumber + NUMBER_CACHE_INFO_EXTENSION_MEMBERS;
171     std::unique_ptr<int64_t[]> cacheInfo = CreateUniquePtr<int64_t[]>(cacheSize);
172     if (cacheInfo == nullptr) {
173         LOGE("[NNCompiledCache] GenerateCacheFiles failed, fail to create cacheInfo instance.");
174         return OH_NN_MEMORY_ERROR;
175     }
176 
177     OH_NN_ReturnCode ret = GenerateCacheModel(caches, cacheInfo, cacheDir, version);
178     if (ret != OH_NN_SUCCESS) {
179         LOGE("[NNCompiledCache] GenerateCacheFiles failed, error happened when calling GenerateCacheModel.");
180         return ret;
181     }
182 
183     uint32_t infoCharNumber = cacheSize * sizeof(uint64_t);
184     ret = WriteCacheInfo(infoCharNumber, cacheInfo, cacheDir);
185     if (ret != OH_NN_SUCCESS) {
186         LOGE("[NNCompiledCache] GenerateCacheFiles failed, error happened when calling WriteCacheInfo.");
187         return ret;
188     }
189 
190     return OH_NN_SUCCESS;
191 }
192 
GenerateCacheModel(const std::vector<OHOS::NeuralNetworkRuntime::Buffer> & caches,std::unique_ptr<int64_t[]> & cacheInfo,const std::string & cacheDir,uint32_t version) const193 OH_NN_ReturnCode NNCompiledCache::GenerateCacheModel(const std::vector<OHOS::NeuralNetworkRuntime::Buffer>& caches,
194                                                      std::unique_ptr<int64_t[]>& cacheInfo,
195                                                      const std::string& cacheDir,
196                                                      uint32_t version) const
197 {
198     size_t cacheNumber = caches.size();
199     if (cacheNumber == 0 || cacheNumber > NN_CACHE_FILE_NUMBER_MAX) {
200         LOGE("[NNCompiledCache] Caches size is equal 0 or greater than 100.");
201         return OH_NN_FAILED;
202     }
203 
204     auto cacheInfoPtr = cacheInfo.get();
205     *cacheInfoPtr++ = static_cast<int64_t>(cacheNumber);
206     *cacheInfoPtr++ = static_cast<int64_t>(version);
207     *cacheInfoPtr++ = static_cast<int64_t>(m_backendID); // Should call SetBackend first.
208 
209     // standardize the input dir
210     OH_NN_ReturnCode ret = OH_NN_SUCCESS;
211     char path[PATH_MAX];
212     if (realpath(cacheDir.c_str(), path) == nullptr) {
213         LOGE("[NNCompiledCache] GenerateCacheModel failed, fail to get the real path of cacheDir.");
214         return OH_NN_INVALID_PARAMETER;
215     }
216 
217     // verify the Standardized path available
218     ret = VerifyCachePath(path);
219     if (ret != OH_NN_SUCCESS) {
220         LOGE("[NNCompiledCache] GenerateCacheModel failed, fail to verify the file path of cacheDir.");
221         return ret;
222     }
223 
224     std::string cachePath = path;
225     for (size_t i = 0; i < cacheNumber; ++i) {
226         std::string cacheModelFile = cachePath + "/" + m_modelName + std::to_string(i) + ".nncache";
227         std::ofstream cacheModelStream(cacheModelFile, std::ios::binary | std::ios::out | std::ios::trunc);
228         if (cacheModelStream.fail()) {
229             LOGE("[NNCompiledCache] GenerateCacheModel failed, model cache file is invalid.");
230             return OH_NN_INVALID_PARAMETER;
231         }
232 
233         uint64_t checkSum =
234             static_cast<int64_t>(GetCrc16(static_cast<char*>(caches[i].data), caches[i].length));
235         *cacheInfoPtr++ = checkSum;
236         if (!cacheModelStream.write(static_cast<const char*>(caches[i].data), caches[i].length)) {
237             LOGE("[NNCompiledCache] GenerateCacheModel failed, fail to write cache model.");
238             cacheModelStream.close();
239             return OH_NN_SAVE_CACHE_EXCEPTION;
240         };
241 
242         cacheModelStream.close();
243     }
244 
245     std::string currentVersion = CURRENT_VERSION;
246     char versionPath[PATH_MAX];
247     if (realpath(HIAI_VERSION_PATH.c_str(), versionPath) != nullptr) {
248         std::ifstream inf(versionPath);
249         if (inf.is_open()) {
250             getline(inf, currentVersion);
251         }
252         inf.close();
253     }
254 
255     int currentOpVersion = std::stoi(currentVersion.substr(OPVERSION_SUBSTR_NUM));
256     *cacheInfoPtr++ = currentOpVersion;
257 
258     LOGI("[NNCompiledCache::GenerateCacheModel] m_isExceedRamLimit: %{public}d", static_cast<int>(m_isExceedRamLimit));
259     if (m_isExceedRamLimit) {
260         *cacheInfoPtr++ = 1;
261     } else {
262         *cacheInfoPtr++ = 0;
263     }
264 
265     return OH_NN_SUCCESS;
266 }
267 
WriteCacheInfo(uint32_t cacheSize,std::unique_ptr<int64_t[]> & cacheInfo,const std::string & cacheDir) const268 OH_NN_ReturnCode NNCompiledCache::WriteCacheInfo(uint32_t cacheSize,
269                                                  std::unique_ptr<int64_t[]>& cacheInfo,
270                                                  const std::string& cacheDir) const
271 {
272     // standardize the input dir
273     char path[PATH_MAX];
274     if (realpath(cacheDir.c_str(), path) == nullptr) {
275         LOGE("[NNCompiledCache] WriteCacheInfo failed, fail to get the real path of cacheDir.");
276         return OH_NN_INVALID_PARAMETER;
277     }
278 
279     // verify the Standardized path available
280     OH_NN_ReturnCode ret = VerifyCachePath(path);
281     if (ret != OH_NN_SUCCESS) {
282         LOGE("[NNCompiledCache] WriteCacheInfo failed, fail to verify the file path of cacheDir.");
283         return ret;
284     }
285 
286     std::string cachePath = path;
287     std::string cacheInfoPath = cachePath + "/" + m_modelName + "cache_info.nncache";
288     std::ofstream cacheInfoStream(cacheInfoPath, std::ios::binary | std::ios::out | std::ios::trunc);
289     if (cacheInfoStream.fail()) {
290         LOGE("[NNCompiledCache] WriteCacheInfo failed, model cache info file is invalid.");
291         return OH_NN_INVALID_FILE;
292     }
293 
294     if (!cacheInfoStream.write(reinterpret_cast<const char*>(cacheInfo.get()), cacheSize)) {
295         LOGE("[NNCompiledCache] WriteCacheInfo failed, fail to write cache info.");
296         cacheInfoStream.close();
297         return OH_NN_SAVE_CACHE_EXCEPTION;
298     }
299 
300     cacheInfoStream.close();
301     return OH_NN_SUCCESS;
302 }
303 
CheckCacheInfo(NNCompiledCacheInfo & modelCacheInfo,const std::string & cacheInfoPath) const304 OH_NN_ReturnCode NNCompiledCache::CheckCacheInfo(NNCompiledCacheInfo& modelCacheInfo,
305                                                  const std::string& cacheInfoPath) const
306 {
307     // cacheInfoPath is validated outside.
308     std::ifstream infoCacheFile(cacheInfoPath.c_str(), std::ios::in | std::ios::binary);
309     if (!infoCacheFile) {
310         LOGE("[NNCompiledCache] CheckCacheInfo failed, error happened when opening cache info file.");
311         return OH_NN_INVALID_FILE;
312     }
313 
314     int charNumber = NUMBER_CACHE_INFO_MEMBERS * sizeof(uint64_t);
315     if (!infoCacheFile.read(reinterpret_cast<char*>(&(modelCacheInfo)), charNumber)) {
316         LOGE("[NNCompiledCache] CheckCacheInfo failed, error happened when reading cache info file.");
317         infoCacheFile.close();
318         return OH_NN_INVALID_FILE;
319     }
320 
321     // modelCacheInfo.deviceId type is int64_t,
322     // it is transformed from size_t value, so the transform here will not truncate value.
323     size_t deviceId = static_cast<size_t>(modelCacheInfo.deviceId);
324     if (deviceId != m_backendID) {
325         LOGE("[NNCompiledCache] CheckCacheInfo failed. The deviceId in the cache files "
326              "is different from current deviceId,"
327              "please change the cache directory or current deviceId.");
328         infoCacheFile.close();
329         return OH_NN_INVALID_PARAMETER;
330     }
331 
332     std::vector<int64_t> modelCheckSum;
333     modelCheckSum.resize(modelCacheInfo.fileNumber);
334     modelCacheInfo.modelCheckSum.resize(modelCacheInfo.fileNumber);
335     if (!infoCacheFile.read(reinterpret_cast<char*>(&modelCheckSum[0]),
336         modelCacheInfo.fileNumber * sizeof(uint64_t))) {
337         LOGE("[NNCompiledCache] CheckCacheInfo failed. The info cache file has been changed.");
338         infoCacheFile.close();
339         return OH_NN_INVALID_FILE;
340     }
341 
342     for (uint32_t i = 0; i < modelCacheInfo.fileNumber; ++i) {
343         modelCacheInfo.modelCheckSum[i] = static_cast<unsigned short>(modelCheckSum[i]);
344     }
345 
346     if (!infoCacheFile.read(reinterpret_cast<char*>(&(modelCacheInfo.opVersion)), sizeof(uint64_t))) {
347         LOGW("[NNCompiledCache] opVersion failed.");
348     }
349 
350     if (!infoCacheFile.read(reinterpret_cast<char*>(&(modelCacheInfo.isExceedRamLimit)), sizeof(uint64_t))) {
351         LOGW("[NNCompiledCache] isExceedRamLimit failed.");
352     }
353 
354     infoCacheFile.close();
355     return OH_NN_SUCCESS;
356 }
357 
ReadCacheModelFile(const std::string & filePath,OHOS::NeuralNetworkRuntime::Buffer & cache) const358 OH_NN_ReturnCode NNCompiledCache::ReadCacheModelFile(const std::string& filePath,
359                                                      OHOS::NeuralNetworkRuntime::Buffer& cache) const
360 {
361     char path[PATH_MAX];
362     if (realpath(filePath.c_str(), path) == nullptr) {
363         LOGE("[NNCompiledCache] ReadCacheModelFile failed, fail to get the real path of filePath.");
364         return OH_NN_INVALID_PARAMETER;
365     }
366     if (access(path, 0) != 0) {
367         LOGE("[NNCompiledCache] ReadCacheModelFile failed, %{public}s is not exist.", path);
368         return OH_NN_INVALID_PARAMETER;
369     }
370 
371     FILE* pFile = fopen(path, "rb");
372     if (pFile == NULL) {
373         LOGE("[NNCompiledCache] ReadCacheModelFile failed, file fopen failed.");
374         return OH_NN_INVALID_FILE;
375     }
376 
377     long fsize{-1};
378     OH_NN_ReturnCode ret = GetCacheFileLength(pFile, fsize);
379     if (ret != OH_NN_SUCCESS) {
380         fclose(pFile);
381         LOGE("[NNCompiledCache] ReadCacheModelFile failed, get file %{public}s length fialed.", filePath.c_str());
382         return ret;
383     }
384 
385     rewind(pFile);
386 
387     char* ptr = static_cast<char*>(m_device->AllocateBuffer(fsize));
388     if (ptr == nullptr) {
389         LOGE("[NNCompiledCache] ReadCacheModelFile failed, failed to allocate memory.");
390         fclose(pFile);
391         return OH_NN_MEMORY_ERROR;
392     }
393 
394     LOGI("ReadCacheModelFile read start.");
395     size_t result = fread(ptr, 1, fsize, pFile); // size of each object in bytes is 1
396     LOGI("ReadCacheModelFile read end.");
397     if (result != static_cast<size_t>(fsize)) {
398         LOGE("[NNCompiledCache] ReadCacheModelFile failed, failed to read file.");
399         fclose(pFile);
400         m_device->ReleaseBuffer(ptr);
401         ptr = nullptr;
402         return OH_NN_INVALID_FILE;
403     }
404 
405     fclose(pFile);
406     cache.data = ptr;
407     cache.length = static_cast<size_t>(fsize); // fsize should be non-negative, safe to cast.
408     return OH_NN_SUCCESS;
409 }
410 
GetCrc16(char * buffer,size_t length) const411 unsigned short NNCompiledCache::GetCrc16(char* buffer, size_t length) const
412 {
413     unsigned int sum = 0;
414     while (length > 1) {
415         sum += *(reinterpret_cast<unsigned short*>(buffer));
416         length -= sizeof(unsigned short);
417         buffer += sizeof(unsigned short);
418     }
419 
420     if (length > 0) {
421         sum += *(reinterpret_cast<unsigned char*>(buffer));
422     }
423 
424     while (sum >> HEX_UNIT) {
425         sum = (sum >> HEX_UNIT) + (sum & 0xffff);
426     }
427 
428     return static_cast<unsigned short>(~sum);
429 }
430 
GetCacheFileLength(FILE * pFile,long & fileSize) const431 OH_NN_ReturnCode NNCompiledCache::GetCacheFileLength(FILE* pFile, long& fileSize) const
432 {
433     int ret = fseek(pFile, 0L, SEEK_END);
434     if (ret != 0) {
435         LOGE("[NNCompiledCache] GetCacheFileLength failed, fail to set the position of the next character "
436              "to be extracted from the input stream.");
437         return OH_NN_FAILED;
438     }
439 
440     long handleValue = ftell(pFile);
441     if (handleValue == -1) {
442         LOGE("[NNCompiledCache] GetCacheFileLength failed, fail to get position of the input stream.");
443         return OH_NN_INVALID_FILE;
444     }
445 
446     if (handleValue == NULL_PTR_LENGTH) {
447         LOGE("[NNCompiledCache] GetCacheFileLength failed, unable to read huge or empty input stream, "
448              "get cache file size=%{public}ld",
449              handleValue);
450         return OH_NN_INVALID_FILE;
451     }
452 
453     fileSize = handleValue;
454     return OH_NN_SUCCESS;
455 }
456 
VerifyCachePath(const std::string & cachePath) const457 OH_NN_ReturnCode NNCompiledCache::VerifyCachePath(const std::string& cachePath) const
458 {
459     // exception: input path is not start with '/'.
460     if (cachePath.find(ROOT_DIR_STR) != size_t(0)) {
461         LOGE("[NNCompiledCache] VerifyCachePath failed, input file dir=%{public}s is invalid, "
462              "should start with '/'.",
463              cachePath.c_str());
464         return OH_NN_INVALID_FILE;
465     }
466 
467     // exception: input path contains continuous double '/'.
468     if (cachePath.find(DOUBLE_SLASH_STR) != std::string::npos) {
469         LOGE("[NNCompiledCache] VerifyCachePath failed, input file dir=%{public}s is invalid, "
470              "containing double '/'.",
471              cachePath.c_str());
472         return OH_NN_INVALID_FILE;
473     }
474 
475     return OH_NN_SUCCESS;
476 }
477 } // namespace NeuralNetworkRuntime
478 } // namespace OHOS
479