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 22namespace panda::ecmascript { 23void 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 35void 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 64void 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 92void 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 130bool 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 171bool 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) 195bool 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 221size_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 229void 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