1 /*
2 * Copyright (c) 2021 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 #include <fcntl.h>
20 #include <sys/mman.h>
21 #include <unistd.h>
22
23 #include "ecmascript/jspandafile/program_object.h"
24 #include "ecmascript/ecma_vm.h"
25 #include "ecmascript/global_env.h"
26 #include "ecmascript/jobs/micro_job_queue.h"
27 #include "ecmascript/js_hclass.h"
28 #include "ecmascript/js_thread.h"
29 #include "ecmascript/mem/c_containers.h"
30 #include "ecmascript/mem/heap.h"
31 #include "ecmascript/object_factory.h"
32 #include "ecmascript/snapshot/mem/snapshot_serialize.h"
33 #include "libpandabase/mem/mem.h"
34
35 namespace panda::ecmascript {
36 constexpr uint32_t PANDA_FILE_ALIGNMENT = 4096;
37
MakeSnapShotProgramObject(Program * program,const panda_file::File * pf,const CString & fileName)38 void SnapShot::MakeSnapShotProgramObject(Program *program, const panda_file::File *pf, const CString &fileName)
39 {
40 std::fstream write;
41 std::pair<bool, CString> filePath = VerifyFilePath(fileName);
42 if (!filePath.first) {
43 LOG(ERROR, RUNTIME) << "snapshot file path error";
44 return;
45 }
46 write.open(filePath.second.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
47 if (!write.good()) {
48 LOG(DEBUG, RUNTIME) << "snapshot open file failed";
49 return;
50 }
51
52 SnapShotSerialize serialize(vm_, true);
53
54 std::unordered_map<uint64_t, SlotBit> data;
55 CQueue<TaggedObject *> objectQueue;
56
57 serialize.RegisterNativeMethod();
58
59 // handle GlobalEnvConstants
60 auto constant = const_cast<GlobalEnvConstants *>(vm_->GetJSThread()->GlobalConstants());
61 constant->VisitRangeSlot([&objectQueue, &data](Root type, ObjectSlot start, ObjectSlot end) {
62 SerializeHelper::AddTaggedObjectRangeToData(start, end, &objectQueue, &data);
63 });
64
65 vm_->Iterate([&objectQueue, &data](Root type, ObjectSlot object) {
66 SerializeHelper::AddObjectHeaderToData(object.GetTaggedObjectHeader(), &objectQueue, &data);
67 });
68
69 while (!objectQueue.empty()) {
70 auto taggedObject = objectQueue.front();
71 if (taggedObject == nullptr) {
72 break;
73 }
74 objectQueue.pop();
75
76 serialize.Serialize(taggedObject, &objectQueue, &data);
77 }
78
79 serialize.SetProgramSerializeStart();
80
81 // handle program
82 if (program != nullptr) {
83 SerializeHelper::AddObjectHeaderToData(program, &objectQueue, &data);
84 }
85
86 while (!objectQueue.empty()) {
87 auto taggedObject = objectQueue.front();
88 if (taggedObject == nullptr) {
89 break;
90 }
91 objectQueue.pop();
92 serialize.Serialize(taggedObject, &objectQueue, &data);
93 }
94
95 serialize.SerializePandaFileMethod();
96
97 vm_->GetHeap()->GetSnapShotSpace()->Stop();
98
99 // write to file
100 SnapShotSpace *space = vm_->GetHeap()->GetSnapShotSpace();
101 size_t defaultSnapshotSpaceCapacity = vm_->GetJSOptions().DefaultSnapshotSpaceCapacity();
102 uint32_t snapshot_size = space->GetRegionCount() * defaultSnapshotSpaceCapacity;
103 uint32_t panda_file_begin = RoundUp(snapshot_size + sizeof(Header), PANDA_FILE_ALIGNMENT);
104 Header hdr {snapshot_size, panda_file_begin};
105 write.write(reinterpret_cast<char *>(&hdr), sizeof(hdr));
106
107 space->EnumerateRegions([&write, &defaultSnapshotSpaceCapacity](Region *current) {
108 write.write(reinterpret_cast<char *>(current), defaultSnapshotSpaceCapacity);
109 write.flush();
110 });
111 space->ReclaimRegions();
112 ASSERT(static_cast<size_t>(write.tellp()) == snapshot_size + sizeof(Header));
113
114 write.seekp(panda_file_begin);
115 write.write(reinterpret_cast<const char *>(pf->GetBase()), pf->GetHeader()->file_size);
116 write.close();
117 }
118
DeserializeGlobalEnvAndProgram(const CString & fileName)119 std::unique_ptr<const panda_file::File> SnapShot::DeserializeGlobalEnvAndProgram(const CString &fileName)
120 {
121 SnapShotSerialize serialize(vm_, false);
122
123 serialize.GeneratedNativeMethod();
124
125 std::pair<bool, CString> filePath = VerifyFilePath(fileName);
126 if (!filePath.first) {
127 LOG(ERROR, RUNTIME) << "snapshot file path error";
128 return std::unique_ptr<const panda_file::File>();
129 }
130
131 int fd = open(filePath.second.c_str(), O_CLOEXEC); // NOLINT(cppcoreguidelines-pro-type-vararg)
132 if (UNLIKELY(fd == -1)) {
133 LOG_ECMA(FATAL) << "open file failed";
134 UNREACHABLE();
135 }
136 size_t file_size = lseek(fd, 0, SEEK_END);
137 auto readFile = ToUintPtr(mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0));
138 auto hdr = *ToNativePtr<const Header>(readFile);
139 size_t defaultSnapshotSpaceCapacity = vm_->GetJSOptions().DefaultSnapshotSpaceCapacity();
140 if (hdr.snapshot_size % defaultSnapshotSpaceCapacity != 0) {
141 LOG_ECMA(FATAL) << "Invalid snapshot file";
142 UNREACHABLE();
143 }
144 SnapShotSpace *space = vm_->GetHeap()->GetSnapShotSpace();
145
146 uintptr_t snapshot_begin = readFile + sizeof(Header);
147 for (size_t i = 0; i < hdr.snapshot_size / defaultSnapshotSpaceCapacity; i++) {
148 Region *region = const_cast<HeapRegionAllocator *>(vm_->GetHeap()->GetHeapRegionAllocator())
149 ->AllocateAlignedRegion(space, defaultSnapshotSpaceCapacity);
150 auto fileRegion = ToNativePtr<Region>(snapshot_begin + i * defaultSnapshotSpaceCapacity);
151
152 uint64_t base = region->allocateBase_;
153 uint64_t begin = (fileRegion->begin_) % defaultSnapshotSpaceCapacity;
154 uint64_t waterMark = (fileRegion->highWaterMark_) % defaultSnapshotSpaceCapacity;
155
156 if (memcpy_s(region, defaultSnapshotSpaceCapacity, fileRegion, defaultSnapshotSpaceCapacity) != EOK) {
157 LOG_ECMA(FATAL) << "memcpy_s failed";
158 UNREACHABLE();
159 }
160
161 // allocate_base_
162 region->allocateBase_ = base;
163 // begin_
164 region->begin_ = ToUintPtr(region) + begin;
165 // end_
166 region->end_ = ToUintPtr(region) + defaultSnapshotSpaceCapacity;
167 // high_water_mark_
168 region->highWaterMark_ = ToUintPtr(region) + waterMark;
169 // prev_
170 region->prev_ = nullptr;
171 // next_
172 region->next_ = nullptr;
173 // mark_bitmap_
174 region->markBitmap_ = nullptr;
175 // cross_region_set_
176 region->crossRegionSet_ = nullptr;
177 // old_to_new_set_
178 region->oldToNewSet_ = nullptr;
179
180 space->AddRegion(region);
181 }
182 munmap(ToNativePtr<void>(readFile), hdr.panda_file_begin);
183 uintptr_t panda_file_mem = readFile + hdr.panda_file_begin;
184 auto pf =
185 panda_file::File::OpenFromMemory(os::mem::ConstBytePtr(ToNativePtr<std::byte>(panda_file_mem),
186 file_size - hdr.panda_file_begin, os::mem::MmapDeleter),
187 fileName);
188 close(fd);
189 // redirect object field
190 serialize.RedirectSlot(pf.get());
191 return pf;
192 }
193
AlignUpPageSize(size_t spaceSize)194 size_t SnapShot::AlignUpPageSize(size_t spaceSize)
195 {
196 if (spaceSize % PAGE_SIZE_ALIGN_UP == 0) {
197 return spaceSize;
198 }
199 return PAGE_SIZE_ALIGN_UP * (spaceSize / PAGE_SIZE_ALIGN_UP + 1);
200 }
201
VerifyFilePath(const CString & filePath)202 std::pair<bool, CString> SnapShot::VerifyFilePath(const CString &filePath)
203 {
204 if (filePath.size() > PATH_MAX) {
205 return std::make_pair(false, "");
206 }
207 CVector<char> resolvedPath(PATH_MAX);
208 auto result = realpath(filePath.c_str(), resolvedPath.data());
209 if (result == resolvedPath.data() || errno == ENOENT) {
210 return std::make_pair(true, CString(resolvedPath.data()));
211 }
212 return std::make_pair(false, "");
213 }
214 } // namespace panda::ecmascript
215