1 /*
2 * Copyright (c) 2022 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/snapshot/mem/snapshot.h"
17
18
19 #include "ecmascript/compiler/pgo_type/pgo_type_manager.h"
20
21 namespace panda::ecmascript {
Serialize(const CString & fileName)22 void Snapshot::Serialize(const CString &fileName)
23 {
24 kungfu::AOTSnapshot& aotSnapshot = vm_->GetPTManager()->GetAOTSnapshot();
25 JSTaggedValue root = aotSnapshot.GetSnapshotData();
26 if (root == JSTaggedValue::Hole()) {
27 // root equals hole means no data stored.
28 LOG_COMPILER(ERROR) << "error: no data for ai file generation!";
29 return;
30 }
31 Serialize(root.GetTaggedObject(), nullptr, fileName);
32 }
33
Serialize(TaggedObject * objectHeader,const JSPandaFile * jsPandaFile,const CString & fileName)34 void Snapshot::Serialize(TaggedObject *objectHeader, const JSPandaFile *jsPandaFile, const CString &fileName)
35 {
36 std::string realPath;
37 if (!RealPath(std::string(fileName), realPath, false)) {
38 LOG_FULL(FATAL) << "snapshot file path error";
39 }
40 std::fstream writer(realPath.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
41 if (!writer.good()) {
42 writer.close();
43 LOG_FULL(ERROR) << "snapshot open file failed";
44 return;
45 }
46
47 SnapshotProcessor processor(vm_);
48 processor.Initialize();
49
50 std::unordered_map<uint64_t, std::pair<uint64_t, EncodeBit>> data;
51 CQueue<TaggedObject *> objectQueue;
52
53 if (objectHeader->GetClass()->GetObjectType() == JSType::PROGRAM) {
54 processor.SetProgramSerializeStart();
55 }
56
57 processor.EncodeTaggedObject(objectHeader, &objectQueue, &data);
58 size_t rootObjSize = objectQueue.size();
59 processor.ProcessObjectQueue(&objectQueue, &data);
60 WriteToFile(writer, jsPandaFile, rootObjSize, processor);
61 }
62
Serialize(uintptr_t startAddr,size_t size,const CString & fileName)63 void Snapshot::Serialize(uintptr_t startAddr, size_t size, const CString &fileName)
64 {
65 std::string realPath;
66 if (!RealPath(std::string(fileName), realPath, false)) {
67 LOG_FULL(FATAL) << "snapshot file path error";
68 }
69 std::fstream writer(realPath.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
70 if (!writer.good()) {
71 writer.close();
72 LOG_FULL(ERROR) << "snapshot open file failed";
73 return;
74 }
75
76 SnapshotProcessor processor(vm_);
77 processor.Initialize();
78
79 std::unordered_map<uint64_t, std::pair<uint64_t, EncodeBit>> data;
80 CQueue<TaggedObject *> objectQueue;
81
82 ObjectSlot start(startAddr);
83 ObjectSlot end(startAddr + size * sizeof(JSTaggedType));
84 processor.EncodeTaggedObjectRange(start, end, &objectQueue, &data);
85
86 size_t rootObjSize = objectQueue.size();
87 processor.ProcessObjectQueue(&objectQueue, &data);
88 WriteToFile(writer, nullptr, rootObjSize, processor);
89 }
90
SerializeBuiltins(const CString & fileName)91 void Snapshot::SerializeBuiltins(const CString &fileName)
92 {
93 std::string realPath;
94 if (!RealPath(std::string(fileName), realPath, false)) {
95 LOG_FULL(FATAL) << "snapshot file path error";
96 }
97 std::fstream write(realPath.c_str(), std::ios::out | std::ios::binary | std::ios::app);
98 if (!write.good()) {
99 write.close();
100 LOG_FULL(ERROR) << "snapshot open file failed";
101 return;
102 }
103 // if builtins.snapshot file has exist, return directly
104 if (write.tellg()) {
105 LOG_FULL(DEBUG) << "snapshot already exist";
106 write.close();
107 return;
108 }
109
110 SnapshotProcessor processor(vm_);
111 processor.Initialize();
112 processor.SetBuiltinsSerializeStart();
113
114 std::unordered_map<uint64_t, std::pair<uint64_t, EncodeBit>> data;
115 CQueue<TaggedObject *> objectQueue;
116
117 class SerializeBuiltinsRootVisitor final : public RootVisitor {
118 public:
119 explicit SerializeBuiltinsRootVisitor(SnapshotProcessor *processor,
120 std::unordered_map<uint64_t, std::pair<uint64_t, EncodeBit>> *data, CQueue<TaggedObject *> *objectQueue)
121 : processor_(processor), data_(data), objectQueue_(objectQueue) {}
122 ~SerializeBuiltinsRootVisitor() = default;
123
124 void VisitRoot([[maybe_unused]] Root type, [[maybe_unused]] ObjectSlot slot) override {}
125 void VisitRangeRoot([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) override
126 {
127 processor_->EncodeTaggedObjectRange(start, end, objectQueue_, data_);
128 }
129 void VisitBaseAndDerivedRoot([[maybe_unused]] Root type, [[maybe_unused]] ObjectSlot base,
130 [[maybe_unused]] ObjectSlot derived, [[maybe_unused]] uintptr_t baseOldObject) override {}
131 private:
132 SnapshotProcessor *processor_;
133 std::unordered_map<uint64_t, std::pair<uint64_t, EncodeBit>> *data_;
134 CQueue<TaggedObject *> *objectQueue_;
135 };
136 SerializeBuiltinsRootVisitor serializeBuiltinsRootVisitor(&processor, &data, &objectQueue);
137
138 auto globalEnvHandle = vm_->GetGlobalEnv();
139 auto constant = const_cast<GlobalEnvConstants *>(vm_->GetJSThread()->GlobalConstants());
140 constant->Iterate(serializeBuiltinsRootVisitor);
141 processor.EncodeTaggedObject(*globalEnvHandle, &objectQueue, &data);
142 size_t rootObjSize = objectQueue.size();
143 processor.ProcessObjectQueue(&objectQueue, &data);
144 WriteToFile(write, nullptr, rootObjSize, processor);
145 }
146
DeserializeInternal(SnapshotType type,const CString & snapshotFile,SnapshotProcessor & processor,MemMap & fileMap)147 bool Snapshot::DeserializeInternal(SnapshotType type, const CString &snapshotFile, SnapshotProcessor &processor,
148 MemMap &fileMap)
149 {
150 if (fileMap.GetOriginAddr() == nullptr) {
151 LOG_FULL(FATAL) << "file mmap failed";
152 UNREACHABLE();
153 }
154 auto readFile = ToUintPtr(fileMap.GetOriginAddr());
155 auto hdr = *ToNativePtr<const SnapShotHeader>(readFile);
156 if (!hdr.Verify(GetLastVersion())) {
157 FileUnMap(fileMap);
158 LOG_ECMA(ERROR) << "file verify failed.";
159 return false;
160 }
161 if (hdr.rootObjectSize == 0) {
162 LOG_ECMA(ERROR) << "Snapshot rootObjectSize is 0";
163 }
164 [[maybe_unused]] EcmaHandleScope stringHandleScope(vm_->GetJSThread());
165 if (g_isEnableCMCGC) {
166 uintptr_t regularObjBegin = readFile + sizeof(SnapShotHeader);
167 uintptr_t stringBegin = regularObjBegin + hdr.regularObjSize + hdr.pinnedObjSize + hdr.largeObjSize;
168 uintptr_t stringEnd = stringBegin + hdr.stringSize;
169 processor.DeserializeString(stringBegin, stringEnd);
170 processor.DeserializeObjectExcludeString(regularObjBegin, hdr.regularObjSize, hdr.pinnedObjSize,
171 hdr.largeObjSize);
172 } else {
173 uintptr_t oldSpaceBegin = readFile + sizeof(SnapShotHeader);
174 uintptr_t stringBegin = oldSpaceBegin + hdr.oldSpaceObjSize + hdr.nonMovableObjSize +
175 hdr.machineCodeObjSize + hdr.snapshotObjSize + hdr.hugeObjSize;
176 uintptr_t stringEnd = stringBegin + hdr.stringSize;
177 processor.DeserializeString(stringBegin, stringEnd);
178 processor.DeserializeObjectExcludeString(oldSpaceBegin, hdr.oldSpaceObjSize, hdr.nonMovableObjSize,
179 hdr.machineCodeObjSize, hdr.snapshotObjSize, hdr.hugeObjSize);
180 }
181
182 #if !defined(CROSS_PLATFORM)
183 FileUnMap(MemMap(fileMap.GetOriginAddr(), hdr.pandaFileBegin));
184 #endif
185 std::shared_ptr<JSPandaFile> jsPandaFile;
186 if (fileMap.GetSize() > hdr.pandaFileBegin) {
187 uintptr_t pandaFileMem = readFile + hdr.pandaFileBegin;
188 auto pf = panda_file::File::OpenFromMemory(os::mem::ConstBytePtr(ToNativePtr<std::byte>(pandaFileMem),
189 fileMap.GetSize() - hdr.pandaFileBegin, os::mem::MmapDeleter));
190 jsPandaFile = JSPandaFileManager::GetInstance()->NewJSPandaFile(pf.release(), "");
191 }
192 // relocate object field
193 processor.Relocate(type, jsPandaFile.get(), hdr.rootObjectSize);
194 processor.AddRootObjectToAOTFileManager(type, snapshotFile);
195 // ONLY used in UT to get the deserialize value result
196 result_ = processor.GetDeserializeResultForUT();
197 LOG_COMPILER(INFO) << "loaded ai file: " << snapshotFile.c_str();
198 return true;
199 }
200
Deserialize(SnapshotType type,const CString & snapshotFile,bool isBuiltins)201 bool Snapshot::Deserialize(SnapshotType type, const CString &snapshotFile, bool isBuiltins)
202 {
203 std::string realPath;
204 if (!RealPath(std::string(snapshotFile), realPath, false)) {
205 LOG_FULL(FATAL) << "snapshot file path error";
206 UNREACHABLE();
207 }
208
209 if (!FileExist(realPath.c_str())) {
210 return false;
211 }
212
213 SnapshotProcessor processor(vm_);
214 if (isBuiltins) {
215 processor.SetBuiltinsDeserializeStart();
216 }
217
218 MemMap fileMap = FileMap(realPath.c_str(), FILE_RDONLY, PAGE_PROT_READWRITE);
219 return DeserializeInternal(type, snapshotFile, processor, fileMap);
220 }
221
222 #if defined(CROSS_PLATFORM) && defined(ANDROID_PLATFORM)
Deserialize(SnapshotType type,const CString & snapshotFile,std::function<bool (std::string fileName,uint8_t ** buff,size_t * buffSize)> ReadAOTCallBack,bool isBuiltins)223 bool Snapshot::Deserialize(SnapshotType type, const CString &snapshotFile, [[maybe_unused]] std::function<bool
224 (std::string fileName, uint8_t **buff, size_t *buffSize)> ReadAOTCallBack, bool isBuiltins)
225 {
226 SnapshotProcessor processor(vm_);
227 if (isBuiltins) {
228 processor.SetBuiltinsDeserializeStart();
229 }
230
231 std::string fileName = std::string(snapshotFile);
232 uint8_t *buff = nullptr;
233 size_t buffSize = 0;
234 MemMap fileMap = {};
235 size_t found = fileName.find_last_of("/");
236 if (found != std::string::npos) {
237 fileName = fileName.substr(found + 1);
238 }
239
240 LOG_ECMA(INFO) << "Call JsAotReader to load: " << fileName;
241 if (ReadAOTCallBack(fileName, &buff, &buffSize)) {
242 fileMap = MemMap(buff, buffSize);
243 }
244
245 return DeserializeInternal(type, snapshotFile, processor, fileMap);
246 }
247 #endif
248
AlignUpPageSize(size_t spaceSize)249 size_t Snapshot::AlignUpPageSize(size_t spaceSize)
250 {
251 if (spaceSize % Constants::PAGE_SIZE_ALIGN_UP == 0) {
252 return spaceSize;
253 }
254 return Constants::PAGE_SIZE_ALIGN_UP * (spaceSize / Constants::PAGE_SIZE_ALIGN_UP + 1);
255 }
256
WriteToFile(std::fstream & writer,const JSPandaFile * jsPandaFile,size_t size,SnapshotProcessor & processor)257 void Snapshot::WriteToFile(std::fstream &writer, const JSPandaFile *jsPandaFile,
258 size_t size, SnapshotProcessor &processor)
259 {
260 size_t totalStringSize = 0U;
261 CVector<uintptr_t> stringVector = processor.GetStringVector();
262 for (size_t i = 0; i < stringVector.size(); ++i) {
263 auto str = reinterpret_cast<EcmaString *>(stringVector[i]);
264 size_t objectSize = AlignUp(EcmaStringAccessor(str).ObjectSize(),
265 static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
266 totalStringSize += objectSize;
267 }
268
269 std::vector<size_t> objSizeVector = processor.StatisticsObjectSize();
270 size_t totalObjSize = totalStringSize;
271 for (size_t objSize : objSizeVector) {
272 totalObjSize += objSize;
273 }
274 size_t pandaFileBegin = RoundUp(totalObjSize + sizeof(SnapShotHeader), Constants::PAGE_SIZE_ALIGN_UP);
275 SnapShotHeader hdr(GetLastVersion());
276 if (g_isEnableCMCGC) {
277 hdr.regularObjSize = objSizeVector[0]; // 0: regularObj
278 hdr.pinnedObjSize = objSizeVector[1]; // 1: pinnedObj
279 hdr.largeObjSize = objSizeVector[2]; // 2: largeObj
280 } else {
281 hdr.oldSpaceObjSize = objSizeVector[0]; // 0: oldSpaceObj
282 hdr.nonMovableObjSize = objSizeVector[1]; // 1: nonMovableObj
283 hdr.machineCodeObjSize = objSizeVector[2]; // 2: machineCodeObj
284 hdr.snapshotObjSize = objSizeVector[3]; // 3: snapshotObj
285 hdr.hugeObjSize = objSizeVector[4]; // 4: hugeObj
286 }
287 hdr.stringSize = totalStringSize;
288 hdr.pandaFileBegin = pandaFileBegin;
289 hdr.rootObjectSize = size;
290 writer.write(reinterpret_cast<char *>(&hdr), sizeof(hdr));
291 processor.WriteObjectToFile(writer);
292
293 for (size_t i = 0; i < stringVector.size(); ++i) {
294 auto str = reinterpret_cast<EcmaString *>(stringVector[i]);
295 size_t strSize = AlignUp(EcmaStringAccessor(str).ObjectSize(),
296 static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
297 int index = 0; // 0 represents the line string.
298 // Write the index in the head of string.
299 uint8_t headerSize = JSTaggedValue::TaggedTypeSize();
300 JSTaggedType indexHeader = JSTaggedValue(index).GetRawData();
301 writer.write(reinterpret_cast<char *>(&indexHeader), headerSize);
302 writer.write(reinterpret_cast<char *>(str) + headerSize, strSize - headerSize);
303 writer.flush();
304 }
305
306 ASSERT(static_cast<size_t>(writer.tellp()) == totalObjSize + sizeof(SnapShotHeader));
307 if (jsPandaFile) {
308 writer.seekp(pandaFileBegin);
309 writer.write(static_cast<const char *>(jsPandaFile->GetHeader()), jsPandaFile->GetFileSize());
310 }
311 writer.close();
312 }
313 } // namespace panda::ecmascript
314