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/module/module_snapshot.h"
17
18 #include "ecmascript/base/config.h"
19 #include "ecmascript/module/js_module_source_text.h"
20 #include "ecmascript/platform/file.h"
21 #include "ecmascript/serializer/module_deserializer.h"
22 #include "ecmascript/serializer/module_serializer.h"
23 #include "securec.h"
24 #include "zlib.h"
25
26 namespace panda::ecmascript {
SerializeDataAndPostSavingJob(const EcmaVM * vm,const CString & path,const CString & version)27 void ModuleSnapshot::SerializeDataAndPostSavingJob(const EcmaVM *vm, const CString &path, const CString &version)
28 {
29 LOG_ECMA(INFO) << "ModuleSnapshot::SerializeDataAndPostSavingJob " << path;
30 ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "ModuleSnapshot::SerializeDataAndPostSavingJob", "");
31 CString filePath = base::ConcatToCString(path, MODULE_SNAPSHOT_FILE_NAME);
32 if (FileExist(filePath.c_str())) {
33 LOG_ECMA(INFO) << "Module serialize file already exist";
34 return;
35 }
36 JSThread *thread = vm->GetJSThread();
37 std::unique_ptr<SerializeData> fileData = GetSerializeData(thread);
38 if (fileData == nullptr) {
39 return;
40 }
41 common::Taskpool::GetCurrentTaskpool()->PostTask(
42 std::make_unique<ModuleSnapshotTask>(thread->GetThreadId(), thread, fileData, filePath, version));
43 }
44
DeserializeData(const EcmaVM * vm,const CString & path,const CString & version)45 bool ModuleSnapshot::DeserializeData(const EcmaVM *vm, const CString &path, const CString &version)
46 {
47 LOG_ECMA(INFO) << "ModuleSnapshot::DeserializeData";
48 ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "ModuleSnapshot::DeserializeData", "");
49 CString filePath = base::ConcatToCString(path, MODULE_SNAPSHOT_FILE_NAME);
50 if (!FileExist(filePath.c_str())) {
51 LOG_ECMA(INFO) << "ModuleSnapshot::DeserializeData Module serialize file doesn't exist: " << path;
52 return false;
53 }
54 JSThread *thread = vm->GetJSThread();
55 std::unique_ptr<SerializeData> fileData = std::make_unique<SerializeData>(thread);
56 if (!ReadDataFromFile(thread, fileData, path, version)) {
57 LOG_ECMA(ERROR) << "ModuleSnapshot::DeserializeData failed: " << filePath;
58 return false;
59 }
60 ModuleDeserializer deserializer(thread, fileData.release());
61 JSHandle<TaggedArray> deserializedModules = JSHandle<TaggedArray>::Cast(deserializer.ReadValue());
62 uint32_t length = deserializedModules->GetLength();
63 for (uint32_t i = 0; i < length; i++) {
64 JSTaggedValue module = deserializedModules->Get(thread, i);
65 JSHandle<SourceTextModule> moduleHdl(thread, SourceTextModule::Cast(module.GetTaggedObject()));
66 CString moduleName = SourceTextModule::GetModuleName(module);
67 if (SourceTextModule::IsSharedModule(moduleHdl)) {
68 SharedModuleManager::GetInstance()->AddToResolvedModulesAndCreateSharedModuleMutex(
69 thread, moduleName, module);
70 continue;
71 }
72 thread->GetModuleManager()->AddResolveImportedModule(moduleName, module);
73 }
74 LOG_ECMA(INFO) << "ModuleSnapshot::DeserializeData success";
75 return true;
76 }
77
GetModuleSerializeArray(JSThread * thread)78 JSHandle<TaggedArray> ModuleSnapshot::GetModuleSerializeArray(JSThread *thread)
79 {
80 ModuleManager *moduleManager = thread->GetModuleManager();
81 uint32_t normalModuleSize = moduleManager->GetResolvedModulesSize();
82 uint32_t sharedModuleSize = SharedModuleManager::GetInstance()->GetResolvedSharedModulesSize();
83 EcmaVM *vm = thread->GetEcmaVM();
84 ObjectFactory *factory = vm->GetFactory();
85 JSHandle<TaggedArray> serializerArray = factory->NewTaggedArray(normalModuleSize + sharedModuleSize);
86 moduleManager->AddNormalSerializeModule(thread, serializerArray, 0); // 0: start index
87 SharedModuleManager::GetInstance()->AddSharedSerializeModule(thread, serializerArray, normalModuleSize);
88 return serializerArray;
89 }
90
RestoreUpdatedBinding(JSThread * thread,JSHandle<TaggedArray> serializeArray)91 void ModuleSnapshot::RestoreUpdatedBinding(JSThread* thread, JSHandle<TaggedArray> serializeArray)
92 {
93 auto globalConstants = thread->GlobalConstants();
94 JSMutableHandle<SourceTextModule> module(thread, globalConstants->GetUndefined());
95 JSMutableHandle<ResolvedIndexBinding> indexBinding(thread, globalConstants->GetUndefined());
96 JSMutableHandle<TaggedArray> environment(thread, globalConstants->GetUndefined());
97 for (uint32_t moduleIdx = 0; moduleIdx < serializeArray->GetLength(); ++moduleIdx) {
98 module.Update(serializeArray->Get(thread, moduleIdx));
99 JSTaggedValue moduleEnvironment = module->GetEnvironment(thread);
100 if (moduleEnvironment.IsUndefined()) {
101 continue;
102 }
103 environment.Update(moduleEnvironment);
104 bool isShared = SourceTextModule::IsSharedModule(module);
105 // check every binding and transfer from ResolvedIndexBinding to ResolvedBinding if binding updated.
106 for (uint32_t bindingIdx = 0; bindingIdx < environment->GetLength(); bindingIdx++) {
107 JSTaggedValue binding = environment->Get(thread, bindingIdx);
108 if (binding.IsResolvedIndexBinding() &&
109 ResolvedIndexBinding::Cast(binding)->GetIsUpdatedFromResolvedBinding()) {
110 indexBinding.Update(binding);
111 JSHandle<JSTaggedValue> nameBinding =
112 SourceTextModule::CreateBindingByIndexBinding(thread, indexBinding, isShared);
113 environment->Set(thread, bindingIdx, nameBinding);
114 }
115 }
116 }
117 }
118
Run(uint32_t threadIndex)119 bool ModuleSnapshot::ModuleSnapshotTask::Run(uint32_t threadIndex)
120 {
121 ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "ModuleSnapshotTask", "");
122 WriteDataToFile(thread_, serializeData_, path_, version_);
123 return true;
124 }
125
RemoveSnapshotFiles(const CString & path)126 void ModuleSnapshot::RemoveSnapshotFiles(const CString &path)
127 {
128 DeleteFilesWithSuffix(path.c_str(), SNAPSHOT_FILE_SUFFIX.data());
129 }
130
GetSerializeData(JSThread * thread)131 std::unique_ptr<SerializeData> ModuleSnapshot::GetSerializeData(JSThread *thread)
132 {
133 ModuleSerializer serializer(thread);
134 JSHandle<TaggedArray> serializeArray = GetModuleSerializeArray(thread);
135 RestoreUpdatedBinding(thread, serializeArray);
136 const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
137 if (!serializer.WriteValue(thread, JSHandle<JSTaggedValue>(serializeArray),
138 globalConstants->GetHandledUndefined(),
139 globalConstants->GetHandledUndefined())) {
140 LOG_ECMA(ERROR) << "ModuleSnapshot::GetSerializeData serialize failed";
141 return nullptr;
142 }
143 std::unique_ptr<SerializeData> fileData = serializer.Release();
144 return fileData;
145 }
146
ReadDataFromFile(JSThread * thread,std::unique_ptr<SerializeData> & data,const CString & path,const CString & version)147 bool ModuleSnapshot::ReadDataFromFile(JSThread *thread, std::unique_ptr<SerializeData>& data, const CString& path,
148 const CString &version)
149 {
150 ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "ModuleSnapshot::ReadDataFromFile", "");
151 CString filePath = base::ConcatToCString(path, MODULE_SNAPSHOT_FILE_NAME);
152 MemMap fileMapMem = FileMap(filePath.c_str(), FILE_RDONLY, PAGE_PROT_READ);
153 if (fileMapMem.GetOriginAddr() == nullptr) {
154 RemoveSnapshotFiles(path);
155 LOG_ECMA(ERROR) << "ModuleSnapshot::ReadDataFromFile File mmap failed";
156 return false;
157 }
158 LOG_ECMA(INFO) << "ModuleSnapshot::ReadDataFromFile";
159 MemMapScope memMapScope(fileMapMem);
160 FileMemMapReader reader(fileMapMem, std::bind(RemoveSnapshotFiles, path), "ModuleSnapshot::ReadDataFromFile");
161 uint32_t checksumSize = sizeof(uint32_t);
162 uint32_t contentSize = fileMapMem.GetSize() - checksumSize;
163 uint32_t readCheckSum = 0;
164 if (!reader.ReadFromOffset(&readCheckSum, checksumSize, contentSize, "checksum")) {
165 return false;
166 }
167 uint32_t checksum = adler32(0, static_cast<const Bytef*>(fileMapMem.GetOriginAddr()), contentSize);
168 if (checksum != readCheckSum) {
169 LOG_ECMA(ERROR) << "ModuleSnapshot::ReadDataFromFile checksum compare failed, checksum: " << checksum
170 << ", readCheckSum" << readCheckSum;
171 RemoveSnapshotFiles(path);
172 return false;
173 }
174 // read app version
175 uint32_t readAppVersionCode = 0;
176 if (!reader.ReadSingleData(&readAppVersionCode, sizeof(readAppVersionCode), "AppVersionCode")) {
177 return false;
178 }
179 uint32_t appVersionCode = thread->GetEcmaVM()->GetApplicationVersionCode();
180 if (readAppVersionCode != appVersionCode) {
181 LOG_ECMA(ERROR) << "ModuleSnapshot::ReadDataFromFile readAppVersionCode: " << readAppVersionCode <<
182 ", appVersionCode: " << appVersionCode << " doesn't match";
183 RemoveSnapshotFiles(path);
184 return false;
185 }
186 // read version
187 uint32_t readVersionStrLen = 0;
188 if (!reader.ReadSingleData(&readVersionStrLen, sizeof(readVersionStrLen), "readVersionStrLen")) {
189 return false;
190 }
191 CString readVersionStr;
192 if (!reader.ReadString(readVersionStr, readVersionStrLen, "readVersionStr")) {
193 return false;
194 }
195 if (version != readVersionStr) {
196 LOG_ECMA(ERROR) << "ModuleSnapshot::ReadDataFromFile version compare failed, version: " << version
197 << ", readVersion" << readVersionStr;
198 RemoveSnapshotFiles(path);
199 return false;
200 }
201 // read dataIndex
202 if (!reader.ReadSingleData(&data->dataIndex_, sizeof(data->dataIndex_), "dataIndex")) {
203 return false;
204 }
205
206 // 8-byte alignment
207 reader.Step(GetAlignUpPadding(reader.GetReadPtr(), fileMapMem.GetOriginAddr(), sizeof(uint64_t)));
208
209 // read uint64_t
210 if (!reader.ReadSingleData(&data->sizeLimit_, sizeof(data->sizeLimit_), "sizeLimit")) {
211 return false;
212 }
213 // 8-byte alignment
214 reader.Step(GetAlignUpPadding(reader.GetReadPtr(), fileMapMem.GetOriginAddr(), sizeof(uint64_t)));
215 // read group size
216 size_t sizeGroup[GROUP_SIZE];
217 if (!reader.ReadSingleData(sizeGroup, GROUP_SIZE * sizeof(size_t), "sizeGroup")) {
218 return false;
219 }
220 data->bufferSize_ = sizeGroup[BUFFER_SIZE_INDEX];
221 data->bufferCapacity_ = sizeGroup[BUFFER_CAPACITY_INDEX];
222 data->regularSpaceSize_ = sizeGroup[REGULAR_SPACE_SIZE_INDEX];
223 data->pinSpaceSize_ = sizeGroup[PIN_SPACE_SIZE_INDEX];
224 data->oldSpaceSize_ = sizeGroup[OLD_SPACE_SIZE_INDEX];
225 data->nonMovableSpaceSize_ = sizeGroup[NONMOVABLE_SPACE_SIZE_INDEX];
226 data->machineCodeSpaceSize_ = sizeGroup[MACHINECODE_SPACE_SIZE_INDEX];
227 data->sharedOldSpaceSize_ = sizeGroup[SHARED_OLD_SPACE_SIZE_INDEX];
228 data->sharedNonMovableSpaceSize_ = sizeGroup[SHARED_NONMOVABLE_SPACE_SIZE_INDEX];
229
230 // read and check imcompleteData
231 const size_t incompleteData = sizeGroup[INCOMPLETE_DATA_INDEX];
232 if (incompleteData != 0) {
233 LOG_ECMA(ERROR) << "ModuleSnapshot::ReadDataFromFile has incompleteData: " << incompleteData;
234 RemoveSnapshotFiles(path);
235 return false;
236 }
237 data->incompleteData_ = (incompleteData != 0);
238
239 if (g_isEnableCMCGC) {
240 // read regularRemainSizeVectorSize
241 size_t regularRemainSizeVectorSize;
242 if (!reader.ReadSingleData(®ularRemainSizeVectorSize, sizeof(regularRemainSizeVectorSize),
243 "regularRemainSizeVectorSize")) {
244 return false;
245 }
246 // read regularRemainSizeVector
247 size_t vecSize = regularRemainSizeVectorSize * sizeof(size_t);
248 if (vecSize > 0) {
249 data->regularRemainSizeVector_.resize(regularRemainSizeVectorSize);
250 if (!reader.ReadSingleData(data->regularRemainSizeVector_.data(), vecSize,
251 "regularRemainSizeVector")) {
252 return false;
253 }
254 }
255 // read pinRemainSizeVectorSize
256 size_t pinRemainSizeVectorSize = 0;
257 if (!reader.ReadSingleData(&pinRemainSizeVectorSize, sizeof(pinRemainSizeVectorSize),
258 "pinRemainSizeVectorSize")) {
259 return false;
260 }
261 // read pinRemainSizeVector
262 vecSize = pinRemainSizeVectorSize * sizeof(size_t);
263 if (vecSize > 0) {
264 data->pinRemainSizeVector_.resize(pinRemainSizeVectorSize);
265 if (!reader.ReadSingleData(data->pinRemainSizeVector_.data(), vecSize, "pinRemainSizeVector")) {
266 return false;
267 }
268 }
269 } else {
270 // read vector size
271 std::array<size_t, SERIALIZE_SPACE_NUM> vecSizes {};
272 uint32_t vecSize = SERIALIZE_SPACE_NUM * sizeof(size_t);
273 if (!reader.ReadSingleData(vecSizes.data(), vecSize, "vecSizes")) {
274 return false;
275 }
276 // read each vector data
277 for (int i = 0; i < SERIALIZE_SPACE_NUM; ++i) {
278 auto& vec = data->regionRemainSizeVectors_[i];
279 const size_t curVectorSize = vecSizes[i];
280 const uint32_t curVectorDataSize = curVectorSize * sizeof(size_t);
281 if (curVectorSize > 0) {
282 vec.resize(curVectorSize);
283 if (!reader.ReadSingleData(vec.data(), curVectorDataSize, "vec")) {
284 return false;
285 }
286 } else {
287 vec.clear();
288 }
289 }
290 }
291
292 // read buffer data
293 if (data->bufferSize_ > 0) {
294 data->buffer_ = static_cast<uint8_t*>(malloc(data->bufferSize_));
295 if (!data->buffer_) {
296 RemoveSnapshotFiles(path);
297 return false;
298 }
299 if (!reader.ReadSingleData(data->buffer_, data->bufferSize_, "buffer")) {
300 return false;
301 }
302 } else {
303 data->buffer_ = nullptr;
304 }
305
306 LOG_ECMA(INFO) << "ModuleSnapshot::ReadDataFromFile success";
307 return true;
308 }
309
WriteDataToFile(JSThread * thread,const std::unique_ptr<SerializeData> & data,const CString & filePath,const CString & version)310 bool ModuleSnapshot::WriteDataToFile(
311 JSThread *thread, const std::unique_ptr<SerializeData>& data, const CString& filePath, const CString &version)
312 {
313 LOG_ECMA(INFO) << "ModuleSnapshot::WriteDataToFile";
314 ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "ModuleSnapshot::WriteDataToFile", "");
315 // calculate file total size
316 // versionCode
317 uint32_t appVersionCode = thread->GetEcmaVM()->GetApplicationVersionCode();
318 uint32_t totalSize = sizeof(appVersionCode);
319 uint32_t versionStrLenSize = sizeof(uint32_t);
320 uint32_t versionStrLen = version.size();
321 totalSize += versionStrLenSize;
322 totalSize += versionStrLen;
323
324 totalSize += sizeof(data->dataIndex_);
325 const size_t alignUp = AlignUp(totalSize, sizeof(uint64_t));
326 totalSize = alignUp + sizeof(data->sizeLimit_);
327
328 // alignment to size_t
329 totalSize = AlignUp(totalSize, sizeof(size_t));
330 // GROUP_SIZE
331 totalSize += GROUP_SIZE * sizeof(size_t);
332
333 if (g_isEnableCMCGC) {
334 totalSize += CMC_GC_REGION_SIZE * sizeof(size_t);
335 totalSize += data->regularRemainSizeVector_.size() * sizeof(size_t);
336 totalSize += data->pinRemainSizeVector_.size() * sizeof(size_t);
337 } else {
338 // vector each element in vector's length
339 totalSize += SERIALIZE_SPACE_NUM * sizeof(size_t);
340
341 // vector data
342 size_t totalVecBytes = 0;
343 for (const auto& vec : data->regionRemainSizeVectors_) {
344 totalVecBytes += vec.size() * sizeof(size_t);
345 }
346 totalSize += totalVecBytes;
347 }
348 // buffer data
349 totalSize += data->bufferSize_;
350 uint32_t checksumSize = sizeof(uint32_t);
351 totalSize += checksumSize;
352 MemMap fileMapMem =
353 CreateFileMap(filePath.c_str(), totalSize, FILE_RDWR | FILE_CREAT | FILE_TRUNC, PAGE_PROT_READWRITE);
354 if (fileMapMem.GetOriginAddr() == nullptr) {
355 LOG_ECMA(ERROR) << "ModuleSnapshot::WriteDataToFile File mmap failed";
356 return false;
357 }
358 MemMapScope memMapScope(fileMapMem);
359 FileMemMapWriter writer(fileMapMem, "ModuleSnapshot::WriteDataToFile");
360
361 // write app versionCode
362 if (!writer.WriteSingleData(&appVersionCode, sizeof(appVersionCode), "appVersionCode")) {
363 return false;
364 }
365 // write version
366 if (!writer.WriteSingleData(&versionStrLen, versionStrLenSize, "versionStrLen")) {
367 return false;
368 }
369 if (!writer.WriteSingleData(version.c_str(), versionStrLen, "versionStr")) {
370 return false;
371 }
372 // write dataIndex
373 if (!writer.WriteSingleData(&data->dataIndex_, sizeof(data->dataIndex_), "dataIndex")) {
374 return false;
375 }
376
377 // padding
378 if (!writer.WriteAlignUpPadding(GetAlignUpPadding(writer.GetWritePtr(),
379 fileMapMem.GetOriginAddr(), sizeof(uint64_t)))) {
380 return false;
381 }
382 // write uint64_t
383 if (!writer.WriteSingleData(&data->sizeLimit_, sizeof(data->sizeLimit_), "sizeLimit")) {
384 return false;
385 }
386
387 // alignment to size_t
388 if (!writer.WriteAlignUpPadding(GetAlignUpPadding(writer.GetWritePtr(),
389 fileMapMem.GetOriginAddr(), sizeof(uint64_t)))) {
390 return false;
391 }
392 // constructor and write GROUP data(size_t)
393 size_t sizeGroup[GROUP_SIZE] = {
394 data->bufferSize_,
395 data->bufferCapacity_,
396 data->regularSpaceSize_,
397 data->pinSpaceSize_,
398 data->oldSpaceSize_,
399 data->nonMovableSpaceSize_,
400 data->machineCodeSpaceSize_,
401 data->sharedOldSpaceSize_,
402 data->sharedNonMovableSpaceSize_,
403 static_cast<size_t>(data->incompleteData_)
404 };
405 if (!writer.WriteSingleData(sizeGroup, GROUP_SIZE * sizeof(size_t), "sizeGroup")) {
406 return false;
407 }
408
409 if (g_isEnableCMCGC) {
410 totalSize += data->regularRemainSizeVector_.size() * sizeof(size_t);
411 totalSize += data->pinRemainSizeVector_.size() * sizeof(size_t);
412 size_t regularRemainSize = data->regularRemainSizeVector_.size();
413 // regularRemainSizeVector size
414 if (!writer.WriteSingleData(®ularRemainSize, sizeof(regularRemainSize), "regularRemainSize")) {
415 return false;
416 }
417 // regularRemainSizeVector
418 size_t regularRemainLen = regularRemainSize * sizeof(size_t);
419 if (regularRemainLen > 0) {
420 if (!writer.WriteSingleData(data->regularRemainSizeVector_.data(), regularRemainLen,
421 "regularRemainSizeVector")) {
422 return false;
423 }
424 }
425 // pinRemainSizeVector size
426 size_t pinRemainSize = data->pinRemainSizeVector_.size();
427 if (!writer.WriteSingleData(&pinRemainSize, sizeof(pinRemainSize), "pinRemainSize")) {
428 return false;
429 }
430 // pinRemainSizeVector
431 size_t pinRemainLen = pinRemainSize * sizeof(size_t);
432 if (pinRemainLen > 0) {
433 if (!writer.WriteSingleData(data->pinRemainSizeVector_.data(), pinRemainLen, "pinRemainSizeVector")) {
434 return false;
435 }
436 }
437 } else {
438 // write vector's size
439 std::array<size_t, SERIALIZE_SPACE_NUM> vecSizes;
440 for (int i = 0; i < SERIALIZE_SPACE_NUM; ++i) {
441 vecSizes[i] = data->regionRemainSizeVectors_[i].size();
442 }
443 uint32_t vecSize = SERIALIZE_SPACE_NUM * sizeof(size_t);
444 if (!writer.WriteSingleData(vecSizes.data(), vecSize, "vecSizes")) {
445 return false;
446 }
447
448 // write vector's data
449 for (const auto& vec : data->regionRemainSizeVectors_) {
450 if (!vec.empty()) {
451 uint32_t curVectorDataSize = vec.size() * sizeof(size_t);
452 if (!writer.WriteSingleData(vec.data(), curVectorDataSize, "vec")) {
453 return false;
454 }
455 }
456 }
457 }
458 // write buffer data
459 if (data->bufferSize_ > 0) {
460 if (!data->buffer_) {
461 return false;
462 }
463 if (!writer.WriteSingleData(data->buffer_, data->bufferSize_, "buffer")) {
464 return false;
465 }
466 }
467 uint32_t contentSize = fileMapMem.GetSize() - checksumSize;
468 uint32_t checksum = adler32(0, static_cast<const Bytef*>(fileMapMem.GetOriginAddr()), contentSize);
469 if (!writer.WriteSingleData(&checksum, checksumSize, "checksum")) {
470 return false;
471 }
472 FileSync(fileMapMem, FILE_MS_SYNC);
473 LOG_ECMA(INFO) << "ModuleSnapshot::WriteDataToFile success";
474 return true;
475 }
476 } // namespace panda::ecmascript
477