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