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_->GetJSThread()->GetCurrentEcmaContext()->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 uintptr_t oldSpaceBegin = readFile + sizeof(SnapShotHeader);
162 uintptr_t stringBegin = oldSpaceBegin + hdr.oldSpaceObjSize + hdr.nonMovableObjSize +
163 hdr.machineCodeObjSize + hdr.snapshotObjSize + hdr.hugeObjSize;
164 uintptr_t stringEnd = stringBegin + hdr.stringSize;
165 [[maybe_unused]] EcmaHandleScope stringHandleScope(vm_->GetJSThread());
166 processor.DeserializeString(stringBegin, stringEnd);
167
168 processor.DeserializeObjectExcludeString(oldSpaceBegin, hdr.oldSpaceObjSize, hdr.nonMovableObjSize,
169 hdr.machineCodeObjSize, hdr.snapshotObjSize, hdr.hugeObjSize);
170
171 #if !defined(CROSS_PLATFORM)
172 FileUnMap(MemMap(fileMap.GetOriginAddr(), hdr.pandaFileBegin));
173 #endif
174 std::shared_ptr<JSPandaFile> jsPandaFile;
175 if (static_cast<uint32_t>(fileMap.GetSize()) > hdr.pandaFileBegin) {
176 uintptr_t pandaFileMem = readFile + hdr.pandaFileBegin;
177 auto pf = panda_file::File::OpenFromMemory(os::mem::ConstBytePtr(ToNativePtr<std::byte>(pandaFileMem),
178 static_cast<uint32_t>(fileMap.GetSize()) - hdr.pandaFileBegin, os::mem::MmapDeleter));
179 jsPandaFile = JSPandaFileManager::GetInstance()->NewJSPandaFile(pf.release(), "");
180 }
181 // relocate object field
182 processor.Relocate(type, jsPandaFile.get(), hdr.rootObjectSize);
183 processor.AddRootObjectToAOTFileManager(type, snapshotFile);
184 LOG_COMPILER(INFO) << "loaded ai file: " << snapshotFile.c_str();
185 return true;
186 }
187
Deserialize(SnapshotType type,const CString & snapshotFile,bool isBuiltins)188 bool Snapshot::Deserialize(SnapshotType type, const CString &snapshotFile, bool isBuiltins)
189 {
190 std::string realPath;
191 if (!RealPath(std::string(snapshotFile), realPath, false)) {
192 LOG_FULL(FATAL) << "snapshot file path error";
193 UNREACHABLE();
194 }
195
196 std::ifstream file(realPath);
197 if (!file.good()) {
198 return false;
199 }
200 file.close();
201
202 SnapshotProcessor processor(vm_);
203 if (isBuiltins) {
204 processor.SetBuiltinsDeserializeStart();
205 }
206
207 MemMap fileMap = FileMap(realPath.c_str(), FILE_RDONLY, PAGE_PROT_READWRITE);
208 return DeserializeInternal(type, snapshotFile, processor, fileMap);
209 }
210
211 #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)212 bool Snapshot::Deserialize(SnapshotType type, const CString &snapshotFile, [[maybe_unused]] std::function<bool
213 (std::string fileName, uint8_t **buff, size_t *buffSize)> ReadAOTCallBack, bool isBuiltins)
214 {
215 SnapshotProcessor processor(vm_);
216 if (isBuiltins) {
217 processor.SetBuiltinsDeserializeStart();
218 }
219
220 std::string fileName = std::string(snapshotFile);
221 uint8_t *buff = nullptr;
222 size_t buffSize = 0;
223 MemMap fileMap = {};
224 size_t found = fileName.find_last_of("/");
225 if (found != std::string::npos) {
226 fileName = fileName.substr(found + 1);
227 }
228
229 LOG_ECMA(INFO) << "Call JsAotReader to load: " << fileName;
230 if (ReadAOTCallBack(fileName, &buff, &buffSize)) {
231 fileMap = MemMap(buff, buffSize);
232 }
233
234 return DeserializeInternal(type, snapshotFile, processor, fileMap);
235 }
236 #endif
237
AlignUpPageSize(size_t spaceSize)238 size_t Snapshot::AlignUpPageSize(size_t spaceSize)
239 {
240 if (spaceSize % Constants::PAGE_SIZE_ALIGN_UP == 0) {
241 return spaceSize;
242 }
243 return Constants::PAGE_SIZE_ALIGN_UP * (spaceSize / Constants::PAGE_SIZE_ALIGN_UP + 1);
244 }
245
WriteToFile(std::fstream & writer,const JSPandaFile * jsPandaFile,size_t size,SnapshotProcessor & processor)246 void Snapshot::WriteToFile(std::fstream &writer, const JSPandaFile *jsPandaFile,
247 size_t size, SnapshotProcessor &processor)
248 {
249 uint32_t totalStringSize = 0U;
250 CVector<uintptr_t> stringVector = processor.GetStringVector();
251 for (size_t i = 0; i < stringVector.size(); ++i) {
252 auto str = reinterpret_cast<EcmaString *>(stringVector[i]);
253 size_t objectSize = AlignUp(EcmaStringAccessor(str).ObjectSize(),
254 static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
255 totalStringSize += objectSize;
256 }
257
258 std::vector<uint32_t> objSizeVector = processor.StatisticsObjectSize();
259 size_t totalObjSize = totalStringSize;
260 for (uint32_t objSize : objSizeVector) {
261 totalObjSize += objSize;
262 }
263 uint32_t pandaFileBegin = RoundUp(totalObjSize + sizeof(SnapShotHeader), Constants::PAGE_SIZE_ALIGN_UP);
264 SnapShotHeader hdr(GetLastVersion());
265 hdr.oldSpaceObjSize = objSizeVector[0]; // 0: oldSpaceObj
266 hdr.nonMovableObjSize = objSizeVector[1]; // 1: nonMovableObj
267 hdr.machineCodeObjSize = objSizeVector[2]; // 2: machineCodeObj
268 hdr.snapshotObjSize = objSizeVector[3]; // 3: snapshotObj
269 hdr.hugeObjSize = objSizeVector[4]; // 4: hugeObj
270 hdr.stringSize = totalStringSize;
271 hdr.pandaFileBegin = pandaFileBegin;
272 hdr.rootObjectSize = static_cast<uint32_t>(size);
273 writer.write(reinterpret_cast<char *>(&hdr), sizeof(hdr));
274 processor.WriteObjectToFile(writer);
275
276 for (size_t i = 0; i < stringVector.size(); ++i) {
277 auto str = reinterpret_cast<EcmaString *>(stringVector[i]);
278 size_t strSize = AlignUp(EcmaStringAccessor(str).ObjectSize(),
279 static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
280 int index = 0; // 0 represents the line string. Natural number 1 represents the constant string.
281 if (EcmaStringAccessor(str).IsConstantString()) {
282 index = 1;
283 }
284 // Write the index in the head of string.
285 uint8_t headerSize = JSTaggedValue::TaggedTypeSize();
286 JSTaggedType indexHeader = JSTaggedValue(index).GetRawData();
287 writer.write(reinterpret_cast<char *>(&indexHeader), headerSize);
288 writer.write(reinterpret_cast<char *>(str) + headerSize, strSize - headerSize);
289 writer.flush();
290 }
291
292 ASSERT(static_cast<size_t>(writer.tellp()) == totalObjSize + sizeof(SnapShotHeader));
293 if (jsPandaFile) {
294 writer.seekp(pandaFileBegin);
295 writer.write(static_cast<const char *>(jsPandaFile->GetHeader()), jsPandaFile->GetFileSize());
296 }
297 writer.close();
298 }
299 } // namespace panda::ecmascript
300