• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 "ecmascript/jspandafile/js_pandafile_snapshot.h"
17 
18 #include "common_components/taskpool/taskpool.h"
19 #include "ecmascript/platform/file.h"
20 #include "ecmascript/platform/filesystem.h"
21 #include "ecmascript/jspandafile/js_pandafile_manager.h"
22 #include "ecmascript/module/module_path_helper.h"
23 #include "zlib.h"
24 
25 namespace panda::ecmascript {
ReadData(JSThread * thread,JSPandaFile * jsPandaFile,const CString & path,const CString & version)26 bool JSPandaFileSnapshot::ReadData(JSThread *thread, JSPandaFile *jsPandaFile, const CString &path,
27     const CString &version)
28 {
29     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "JSPandaFile::ReadData", "");
30     // check application white list & specific file
31     if (IsJSPandaFileSnapshotFileExist(jsPandaFile->GetJSPandaFileDesc(), path)) {
32         return ReadDataFromFile(thread, jsPandaFile, path, version);
33     }
34     return false;
35 }
36 
PostWriteDataToFileJob(const EcmaVM * vm,const CString & path,const CString & version)37 void JSPandaFileSnapshot::PostWriteDataToFileJob(const EcmaVM *vm, const CString &path, const CString &version)
38 {
39     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "JSPandaFileSnapshot::PostWriteDataToFileJob", "");
40     LOG_ECMA(INFO) << "JSPandaFileSnapshot::PostWriteDataToFileJob";
41     std::unordered_set<std::shared_ptr<JSPandaFile>> jspandaFiles =
42         JSPandaFileManager::GetInstance()->GetHapJSPandaFiles();
43     JSThread *thread = vm->GetJSThread();
44     int32_t tid = static_cast<int32_t>(thread->GetThreadId());
45     for (const auto &item : jspandaFiles) {
46         common::Taskpool::GetCurrentTaskpool()->PostTask(
47             std::make_unique<JSPandaFileSnapshotTask>(tid, thread, item.get(), path, version));
48     }
49 }
50 
Run(uint32_t threadIndex)51 bool JSPandaFileSnapshot::JSPandaFileSnapshotTask::Run(uint32_t threadIndex)
52 {
53     WriteDataToFile(thread_, jsPandaFile_, path_, version_);
54     return true;
55 }
56 
IsJSPandaFileSnapshotFileExist(const CString & fileName,const CString & path)57 bool JSPandaFileSnapshot::IsJSPandaFileSnapshotFileExist(const CString &fileName, const CString &path)
58 {
59     CString serializeFileName = GetJSPandaFileFileName(fileName, path);
60     return FileExist(serializeFileName.c_str());
61 }
62 
GetJSPandaFileFileName(const CString & fileName,const CString & path)63 CString JSPandaFileSnapshot::GetJSPandaFileFileName(const CString &fileName, const CString &path)
64 {
65     CString moduleName = ModulePathHelper::GetModuleNameWithBaseFile(fileName);
66     return base::ConcatToCString(path, moduleName, JSPANDAFILE_FILE_NAME);
67 }
68 
RemoveSnapshotFiles(const CString & path)69 void JSPandaFileSnapshot::RemoveSnapshotFiles(const CString &path)
70 {
71     DeleteFilesWithSuffix(path.c_str(), SNAPSHOT_FILE_SUFFIX.data());
72 }
73 
WriteDataToFile(JSThread * thread,JSPandaFile * jsPandaFile,const CString & path,const CString & version)74 bool JSPandaFileSnapshot::WriteDataToFile(JSThread *thread, JSPandaFile *jsPandaFile, const CString &path,
75     const CString &version)
76 {
77     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "JSPandaFileSnapshot::WriteDataToFile", "");
78     CString filename = GetJSPandaFileFileName(jsPandaFile->GetJSPandaFileDesc(), path);
79     if (FileExist(filename.c_str())) {
80         LOG_ECMA(INFO) << "JSPandaFileSnapshot::WriteDataToFile file already exist";
81         return false;
82     }
83     LOG_ECMA(INFO) << "JSPandaFileSnapshot::WriteDataToFile: " << filename;
84     // calculate file size
85     uint32_t checksumSize = sizeof(uint32_t);
86     uint32_t fileSize = sizeof(uint32_t);
87     uint32_t appVersionCodeSize = sizeof(uint32_t);
88     uint32_t versionStrLenSize = sizeof(uint32_t);
89     uint32_t versionStrLen = version.size();
90     size_t bufSize = appVersionCodeSize + versionStrLenSize + versionStrLen + fileSize + checksumSize;
91     // add moduleName len & ptr
92     CString moduleName = ModulePathHelper::GetModuleNameWithBaseFile(jsPandaFile->GetJSPandaFileDesc());
93     uint32_t moduleNameLen = moduleName.size();
94     bufSize += sizeof(uint32_t); // len
95     bufSize += moduleNameLen; // ptr
96     // add methodLiteral size
97     uint32_t numMethods = jsPandaFile->numMethods_;
98     bufSize += sizeof(numMethods); // numMethods
99     bufSize += sizeof(MethodLiteral) * numMethods; // MethodLiteral Total size
100     // calculate mainMethodIndex size
101     uint32_t mainMethodIndexSize = 0;
102     bufSize += sizeof(mainMethodIndexSize);
103     // recordName size
104     uint32_t mainMethodIndex = JSPandaFile::DEFAULT_MAIN_METHOD_INDEX;
105     for (auto &[recordName, recordInfo]: jsPandaFile->GetJSRecordInfo()) {
106         mainMethodIndex = recordInfo->mainMethodIndex;
107         if (mainMethodIndex != JSPandaFile::DEFAULT_MAIN_METHOD_INDEX) {
108             bufSize += sizeof(uint32_t); // mainMethodIndex
109             bufSize += sizeof(uint32_t); // recordName len
110             bufSize += recordName.size(); // recordName ptr
111             mainMethodIndexSize++;
112         }
113     }
114 
115     MemMap fileMapMem =
116         CreateFileMap(filename.c_str(), bufSize, FILE_RDWR | FILE_CREAT | FILE_TRUNC, PAGE_PROT_READWRITE);
117     if (fileMapMem.GetOriginAddr() == nullptr) {
118         LOG_ECMA(ERROR) << "JSPandaFileSnapshot::WriteDataToFile open file failed:" << filename;
119         return false;
120     }
121     MemMapScope memMapScope(fileMapMem);
122     FileMemMapWriter writer(fileMapMem, "JSPandaFileSnapshot::WriteDataToFile");
123     // write versionCode
124     uint32_t appVersionCode = thread->GetEcmaVM()->GetApplicationVersionCode();
125     if (!writer.WriteSingleData(&appVersionCode, sizeof(appVersionCode), "appVersionCode")) {
126         return false;
127     }
128     if (!writer.WriteSingleData(&versionStrLen, sizeof(versionStrLen), "versionStrLen")) {
129         return false;
130     }
131     if (!writer.WriteSingleData(version.c_str(), versionStrLen, "versionStr")) {
132         return false;
133     }
134     // write pandafile size
135     uint32_t fsize = jsPandaFile->GetFileSize();
136     if (!writer.WriteSingleData(&fsize, sizeof(fsize), "fsize")) {
137         return false;
138     }
139     // write moduleName
140     if (!writer.WriteSingleData(&moduleNameLen, sizeof(moduleNameLen), "moduleNameLen")) {
141         return false;
142     }
143     if (!writer.WriteSingleData(moduleName.c_str(), moduleNameLen, "moduleName")) {
144         return false;
145     }
146     // write numMethods
147     if (!writer.WriteSingleData(&numMethods, sizeof(numMethods), "numMethods")) {
148         return false;
149     }
150     size_t methodLiteralSize = sizeof(MethodLiteral) * numMethods;
151     // write MethodLiterals
152     if (!writer.WriteSingleData(jsPandaFile->GetMethodLiterals(), methodLiteralSize, "methodLiterals")) {
153         return false;
154     }
155     MethodLiteral *methodLiterals = reinterpret_cast<MethodLiteral*>(writer.GetWritePtr() - methodLiteralSize);
156     size_t methodIdx = 0;
157     // get current pandafile base offset
158     uintptr_t baseAddress = reinterpret_cast<uintptr_t>(jsPandaFile->GetBase());
159     while (methodIdx < jsPandaFile->numMethods_) {
160         MethodLiteral *methodLiteral = methodLiterals + (methodIdx++);
161         uintptr_t nativePointerAddress = reinterpret_cast<uintptr_t>(methodLiteral->GetNativePointer());
162         // calculate relative offset
163         methodLiteral->SetNativePointer(reinterpret_cast<void*>(nativePointerAddress - baseAddress));
164     }
165     // write mainMethodIndexSize
166     if (!writer.WriteSingleData(&mainMethodIndexSize, sizeof(mainMethodIndexSize), "mainMethodIndexSize")) {
167         return false;
168     }
169     // write mainMethodIndex & recordName
170     mainMethodIndex = JSPandaFile::DEFAULT_MAIN_METHOD_INDEX;
171     for (auto &[recordName, recordInfo]: jsPandaFile->GetJSRecordInfo()) {
172         mainMethodIndex = recordInfo->mainMethodIndex;
173         if (mainMethodIndex != JSPandaFile::DEFAULT_MAIN_METHOD_INDEX) {
174             if (!writer.WriteSingleData(&mainMethodIndex, sizeof(mainMethodIndex), "mainMethodIndex")) {
175                 return false;
176             }
177             uint32_t recordNameLen = recordName.size();
178             if (!writer.WriteSingleData(&recordNameLen, sizeof(recordNameLen), "recordNameLen")) {
179                 return false;
180             }
181             if (!writer.WriteSingleData(recordName.data(), recordNameLen, "recordName")) {
182                 return false;
183             }
184         }
185     }
186     uint32_t contentSize = fileMapMem.GetSize() - checksumSize;
187     uint32_t checksum = adler32(0, static_cast<const Bytef*>(fileMapMem.GetOriginAddr()), contentSize);
188     if (!writer.WriteSingleData(&checksum, checksumSize, "checksum")) {
189         return false;
190     }
191     FileSync(fileMapMem, FILE_MS_SYNC);
192     return true;
193 }
194 
ReadDataFromFile(JSThread * thread,JSPandaFile * jsPandaFile,const CString & path,const CString & version)195 bool JSPandaFileSnapshot::ReadDataFromFile(JSThread *thread, JSPandaFile *jsPandaFile, const CString &path,
196     const CString &version)
197 {
198     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "JSPandaFileSnapshot::ReadDataFromFile", "");
199     CString filename = GetJSPandaFileFileName(jsPandaFile->GetJSPandaFileDesc(), path);
200     MemMap fileMapMem = FileMap(filename.c_str(), FILE_RDONLY, PAGE_PROT_READ, 0);
201     if (fileMapMem.GetOriginAddr() == nullptr) {
202         RemoveSnapshotFiles(path);
203         LOG_ECMA(ERROR) << "JSPandaFileSnapshot::ReadDataFromFile open file failed:" << filename;
204         return false;
205     }
206     LOG_ECMA(INFO) << "JSPandaFileSnapshot::ReadDataFromFile: " << filename;
207     MemMapScope memMapScope(fileMapMem);
208     FileMemMapReader reader(fileMapMem, std::bind(RemoveSnapshotFiles, path), "JSPandaFileSnapshot::ReadDataFromFile");
209 
210     size_t checksumSize = sizeof(uint32_t);
211     uint32_t contentSize = fileMapMem.GetSize() - checksumSize;
212     uint32_t readCheckSum = 0;
213     if (!reader.ReadFromOffset(&readCheckSum, checksumSize, contentSize, "checksum")) {
214         return false;
215     }
216     uint32_t checksum = adler32(0, static_cast<const Bytef*>(fileMapMem.GetOriginAddr()), contentSize);
217     if (checksum != readCheckSum) {
218         LOG_ECMA(ERROR) << "JSPandaFileSnapshot::ReadDataFromFile checksum compare failed, checksum: " << checksum
219             << ", readCheckSum: " << readCheckSum;
220         RemoveSnapshotFiles(path);
221         return false;
222     }
223     // verify version code
224     uint32_t appVersionCode = thread->GetEcmaVM()->GetApplicationVersionCode();
225     uint32_t readAppVersionCode = 0;
226     if (!reader.ReadSingleData(&readAppVersionCode, sizeof(readAppVersionCode), "appVersionCode")) {
227         return false;
228     }
229     if (appVersionCode != readAppVersionCode) {
230         LOG_ECMA(ERROR) << "JSPandaFileSnapshot::ReadDataFromFile version compare failed, appVersionCode: "
231             << appVersionCode << ", readAppVersionCode: " << readAppVersionCode;
232         RemoveSnapshotFiles(path);
233         return false;
234     }
235     uint32_t readVersionStrLen = 0;
236     if (!reader.ReadSingleData(&readVersionStrLen, sizeof(readVersionStrLen), "readVersionStrLen")) {
237         return false;
238     }
239     CString readVersionStr;
240     if (!reader.ReadString(readVersionStr, readVersionStrLen, "readVersionStr")) {
241         return false;
242     }
243     if (version != readVersionStr) {
244         LOG_ECMA(ERROR) << "JSPandaFileSnapshot::ReadDataFromFile version compare failed, version: " << version
245             << ", readVersion: " << readVersionStr;
246         RemoveSnapshotFiles(path);
247         return false;
248     }
249     // verify filesize
250     uint32_t fsize = 0;
251     if (!reader.ReadSingleData(&fsize, sizeof(fsize), "fsize")) {
252         return false;
253     }
254     if (fsize != jsPandaFile->GetFileSize()) {
255         LOG_COMPILER(ERROR) << "JSPandaFileSnapshot::ReadDataFromFile file size not equal, " << filename  << ", old = "
256             << fsize << ", new = " << jsPandaFile->GetFileSize();
257         RemoveSnapshotFiles(path);
258         return false;
259     }
260     // verify moduleName
261     // read moduleName len
262     uint32_t moduleNameLen = 0;
263     if (!reader.ReadSingleData(&moduleNameLen, sizeof(moduleNameLen), "moduleNameLen")) {
264         return false;
265     }
266     // Get moduleName
267     CString readModuleName;
268     if (!reader.ReadString(readModuleName, moduleNameLen, "readModuleName")) {
269         return false;
270     }
271     CString curModuleName = ModulePathHelper::GetModuleNameWithBaseFile(jsPandaFile->GetJSPandaFileDesc());
272     if (readModuleName != curModuleName) {
273         LOG_ECMA(ERROR) << "JSPandaFileSnapshot::ReadDataFromFile moduleName check failed, read moduleName is: "
274             << readModuleName << ", current moduleName is:" << curModuleName;
275         RemoveSnapshotFiles(path);
276         return false;
277     }
278     // read numMethods
279     uint32_t numMethods = 0;
280     if (!reader.ReadSingleData(&numMethods, sizeof(numMethods), "numMethods")) {
281         return false;
282     }
283     jsPandaFile->numMethods_ = numMethods;
284     // read MethodLiterals
285     MethodLiteral *methodLiterals = jsPandaFile->GetMethodLiterals();
286     size_t methodLiteralSize = numMethods * sizeof(MethodLiteral);
287     if (!reader.ReadSingleData(methodLiterals, methodLiteralSize, "methodLiteralSize")) {
288         return false;
289     }
290     size_t methodIdx = 0;
291     // get current pandafile base offset
292     uintptr_t baseAddress = reinterpret_cast<uintptr_t>(jsPandaFile->GetBase());
293     while (methodIdx < numMethods) {
294         MethodLiteral *methodLiteral = methodLiterals + (methodIdx++);
295         uintptr_t nativePointerOffset = reinterpret_cast<uintptr_t>(methodLiteral->GetNativePointer());
296         // calculate relative offset
297         methodLiteral->SetNativePointer(reinterpret_cast<void*>(nativePointerOffset + baseAddress));
298     }
299     jsPandaFile->SetAllMethodLiteralToMap();
300 
301     // read needUpdate size
302     uint32_t mainMethodIndexSize = 0;
303     if (!reader.ReadSingleData(&mainMethodIndexSize, sizeof(mainMethodIndexSize), "mainMethodIndexSize")) {
304         return false;
305     }
306     // calculte mainMethodIndex and recordName
307     uint32_t recordNum = 0;
308     while (recordNum < mainMethodIndexSize) {
309         // Get mainMethodIndex
310         uint32_t mainMethodIndex = 0;
311         if (!reader.ReadSingleData(&mainMethodIndex, sizeof(mainMethodIndex), "mainMethodIndex")) {
312             return false;
313         }
314         // Get recordName length
315         uint32_t recordNameLen = 0;
316         if (!reader.ReadSingleData(&recordNameLen, sizeof(recordNameLen), "recordNameLen")) {
317             return false;
318         }
319         // Get recordName
320         CString recordName;
321         if (!reader.ReadString(recordName, recordNameLen, "recordName")) {
322             return false;
323         }
324         // set mainMethodIndex record and index
325         auto info = jsPandaFile->jsRecordInfo_.find(recordName);
326         if (info != jsPandaFile->jsRecordInfo_.end()) {
327             info->second->mainMethodIndex = mainMethodIndex;
328         }
329         recordNum++;
330     }
331     LOG_ECMA(INFO) << "JSPandaFileSnapshot::ReadDataFromFile success with: " << filename;
332     return true;
333 }
334 } // namespace panda::ecmascript