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