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 std::ifstream file(realPath);
180 if (!file.good()) {
181 return false;
182 }
183 file.close();
184
185 SnapshotProcessor processor(vm_);
186 if (isBuiltins) {
187 processor.SetBuiltinsDeserializeStart();
188 }
189
190 MemMap fileMap = FileMap(realPath.c_str(), FILE_RDONLY, PAGE_PROT_READWRITE);
191 return DeserializeInternal(type, snapshotFile, processor, fileMap);
192 }
193
194 #if defined(CROSS_PLATFORM) && defined(ANDROID_PLATFORM)
Deserialize(SnapshotType type, const CString &snapshotFile, [[maybe_unused]] std::function<bool (std::string fileName, uint8_t **buff, size_t *buffSize)> ReadAOTCallBack, bool isBuiltins)195 bool Snapshot::Deserialize(SnapshotType type, const CString &snapshotFile, [[maybe_unused]] std::function<bool
196 (std::string fileName, uint8_t **buff, size_t *buffSize)> ReadAOTCallBack, bool isBuiltins)
197 {
198 SnapshotProcessor processor(vm_);
199 if (isBuiltins) {
200 processor.SetBuiltinsDeserializeStart();
201 }
202
203 std::string fileName = std::string(snapshotFile);
204 uint8_t *buff = nullptr;
205 size_t buffSize = 0;
206 MemMap fileMap = {};
207 size_t found = fileName.find_last_of("/");
208 if (found != std::string::npos) {
209 fileName = fileName.substr(found + 1);
210 }
211
212 LOG_ECMA(INFO) << "Call JsAotReader to load: " << fileName;
213 if (ReadAOTCallBack(fileName, &buff, &buffSize)) {
214 fileMap = MemMap(buff, buffSize);
215 }
216
217 return DeserializeInternal(type, snapshotFile, processor, fileMap);
218 }
219 #endif
220
AlignUpPageSize(size_t spaceSize)221 size_t Snapshot::AlignUpPageSize(size_t spaceSize)
222 {
223 if (spaceSize % Constants::PAGE_SIZE_ALIGN_UP == 0) {
224 return spaceSize;
225 }
226 return Constants::PAGE_SIZE_ALIGN_UP * (spaceSize / Constants::PAGE_SIZE_ALIGN_UP + 1);
227 }
228
WriteToFile(std::fstream &writer, const JSPandaFile *jsPandaFile, size_t size, SnapshotProcessor &processor)229 void Snapshot::WriteToFile(std::fstream &writer, const JSPandaFile *jsPandaFile,
230 size_t size, SnapshotProcessor &processor)
231 {
232 uint32_t totalStringSize = 0U;
233 CVector<uintptr_t> stringVector = processor.GetStringVector();
234 for (size_t i = 0; i < stringVector.size(); ++i) {
235 auto str = reinterpret_cast<EcmaString *>(stringVector[i]);
236 size_t objectSize = AlignUp(EcmaStringAccessor(str).ObjectSize(),
237 static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
238 totalStringSize += objectSize;
239 }
240
241 std::vector<uint32_t> objSizeVector = processor.StatisticsObjectSize();
242 size_t totalObjSize = totalStringSize;
243 for (uint32_t objSize : objSizeVector) {
244 totalObjSize += objSize;
245 }
246 uint32_t pandaFileBegin = RoundUp(totalObjSize + sizeof(SnapShotHeader), Constants::PAGE_SIZE_ALIGN_UP);
247 SnapShotHeader hdr(GetLastVersion());
248 hdr.oldSpaceObjSize = objSizeVector[0]; // 0: oldSpaceObj
249 hdr.nonMovableObjSize = objSizeVector[1]; // 1: nonMovableObj
250 hdr.machineCodeObjSize = objSizeVector[2]; // 2: machineCodeObj
251 hdr.snapshotObjSize = objSizeVector[3]; // 3: snapshotObj
252 hdr.hugeObjSize = objSizeVector[4]; // 4: hugeObj
253 hdr.stringSize = totalStringSize;
254 hdr.pandaFileBegin = pandaFileBegin;
255 hdr.rootObjectSize = static_cast<uint32_t>(size);
256 writer.write(reinterpret_cast<char *>(&hdr), sizeof(hdr));
257 processor.WriteObjectToFile(writer);
258
259 for (size_t i = 0; i < stringVector.size(); ++i) {
260 auto str = reinterpret_cast<EcmaString *>(stringVector[i]);
261 size_t strSize = AlignUp(EcmaStringAccessor(str).ObjectSize(),
262 static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
263 int index = 0; // 0 represents the line string. Natural number 1 represents the constant string.
264 if (EcmaStringAccessor(str).IsConstantString()) {
265 index = 1;
266 }
267 // Write the index in the head of string.
268 uint8_t headerSize = JSTaggedValue::TaggedTypeSize();
269 JSTaggedType indexHeader = JSTaggedValue(index).GetRawData();
270 writer.write(reinterpret_cast<char *>(&indexHeader), headerSize);
271 writer.write(reinterpret_cast<char *>(str) + headerSize, strSize - headerSize);
272 writer.flush();
273 }
274
275 ASSERT(static_cast<size_t>(writer.tellp()) == totalObjSize + sizeof(SnapShotHeader));
276 if (jsPandaFile) {
277 writer.seekp(pandaFileBegin);
278 writer.write(static_cast<const char *>(jsPandaFile->GetHeader()), jsPandaFile->GetFileSize());
279 }
280 writer.close();
281 }
282 } // namespace panda::ecmascript
283