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