1 /*
2 * Copyright (c) 2023 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 #include "ecmascript/compiler/aot_file/an_file_data_manager.h"
16 #include "ecmascript/js_file_path.h"
17 #include "ecmascript/mem/c_string.h"
18 #include "ecmascript/platform/file.h"
19
20 namespace panda::ecmascript {
GetInstance()21 AnFileDataManager *AnFileDataManager::GetInstance()
22 {
23 static AnFileDataManager *anFileDataManager = new AnFileDataManager();
24 return anFileDataManager;
25 }
26
~AnFileDataManager()27 AnFileDataManager::~AnFileDataManager()
28 {
29 SafeDestroyAllData();
30 }
31
DestroyFileMapMem(MemMap & fileMapMem)32 void AnFileDataManager::DestroyFileMapMem(MemMap &fileMapMem)
33 {
34 if (fileMapMem.GetOriginAddr() != nullptr && fileMapMem.GetSize() > 0) {
35 FileUnMap(fileMapMem);
36 fileMapMem.Reset();
37 }
38 }
39
SafeDestroyAllData()40 void AnFileDataManager::SafeDestroyAllData()
41 {
42 WriteLockHolder lock(lock_);
43 if (loadedStub_ != nullptr) {
44 loadedStub_->UnregisterFromDebugger();
45 ExecutedMemoryAllocator::DestroyBuf(loadedStub_->GetStubsMem());
46 DestroyFileMapMem(loadedStub_->GetFileMapMem());
47 loadedStub_ = nullptr;
48 }
49
50 for (auto &iter : loadedAn_) {
51 DestroyFileMapMem(iter->GetFileMapMem());
52 }
53 loadedAn_.clear();
54 anFileNameToIndexMap_.clear();
55 }
56
SafeDestroyAnData(const std::string & fileName)57 void AnFileDataManager::SafeDestroyAnData(const std::string &fileName)
58 {
59 WriteLockHolder lock(lock_);
60 std::string anBasename = JSFilePath::GetBaseName(fileName);
61 auto index = UnSafeGetFileInfoIndex(anBasename);
62 if (index == INVALID_INDEX) {
63 return;
64 }
65 auto info = UnSafeGetAnFileInfo(index);
66 info->Destroy();
67 }
68
SafeLoad(const std::string & fileName,Type type,std::function<bool (std::string fileName,uint8_t ** buff,size_t * buffSize)> cb)69 bool AnFileDataManager::SafeLoad(const std::string &fileName, Type type, [[maybe_unused]] std::function<bool
70 (std::string fileName, uint8_t **buff, size_t *buffSize)> cb)
71 {
72 WriteLockHolder lock(lock_);
73 if (type == Type::STUB) {
74 if (loadedStub_ != nullptr) {
75 return true;
76 }
77 return UnsafeLoadFromStub(fileName);
78 } else {
79 const std::shared_ptr<const AOTFileInfo> aotFileInfo = UnsafeFind(fileName);
80 if (aotFileInfo != nullptr) {
81 return true;
82 }
83 #if defined(CROSS_PLATFORM) && defined(ANDROID_PLATFORM)
84 return UnsafeLoadFromAOT(fileName, cb);
85 #else
86 return UnsafeLoadFromAOT(fileName);
87 #endif
88 }
89 }
90
UnsafeFind(const std::string & fileName) const91 std::shared_ptr<AnFileInfo> AnFileDataManager::UnsafeFind(const std::string &fileName) const
92 {
93 // note: This method is not thread-safe
94 // need to ensure that the instance of AnFileDataManager has been locked before use
95 const auto iter = anFileNameToIndexMap_.find(fileName);
96 if (iter == anFileNameToIndexMap_.end()) {
97 return nullptr;
98 }
99 uint32_t index = iter->second;
100 return loadedAn_.at(index);
101 }
102
UnsafeLoadFromStub(const std::string & fileName)103 bool AnFileDataManager::UnsafeLoadFromStub(const std::string &fileName)
104 {
105 // note: This method is not thread-safe
106 // need to ensure that the instance of AnFileDataManager has been locked before use
107 loadedStub_ = std::make_shared<StubFileInfo>(StubFileInfo());
108 if (!fileName.empty()) {
109 return loadedStub_->MmapLoad(fileName);
110 }
111 #if defined(PANDA_TARGET_OHOS)
112 return loadedStub_->MmapLoad(fileName);
113 #else
114 return loadedStub_->Load();
115 #endif
116 }
117
Dump() const118 void AnFileDataManager::Dump() const
119 {
120 loadedStub_->Dump();
121 for (const auto& an : loadedAn_) {
122 an->Dump();
123 }
124 }
125
UnsafeLoadFromAOTInternal(const std::string & fileName,std::shared_ptr<AnFileInfo> & info)126 bool AnFileDataManager::UnsafeLoadFromAOTInternal(const std::string &fileName, std::shared_ptr<AnFileInfo> &info)
127 {
128 std::string anBasename = JSFilePath::GetBaseName(fileName);
129 anFileNameToIndexMap_.insert({anBasename, loadedAn_.size()});
130 loadedAn_.emplace_back(info);
131 return true;
132 }
133
UnsafeLoadFromAOT(const std::string & fileName)134 bool AnFileDataManager::UnsafeLoadFromAOT(const std::string &fileName)
135 {
136 // note: This method is not thread-safe
137 // need to ensure that the instance of AnFileDataManager has been locked before use
138 std::shared_ptr<AnFileInfo> info = std::make_shared<AnFileInfo>(AnFileInfo());
139 if (!info->Load(fileName)) {
140 return false;
141 }
142 return UnsafeLoadFromAOTInternal(fileName, info);
143 }
144
145 #if defined(CROSS_PLATFORM) && defined(ANDROID_PLATFORM)
UnsafeLoadFromAOT(const std::string & fileName,std::function<bool (std::string fileName,uint8_t ** buff,size_t * buffSize)> cb)146 bool AnFileDataManager::UnsafeLoadFromAOT(const std::string &fileName, std::function<bool
147 (std::string fileName, uint8_t **buff, size_t *buffSize)> cb)
148 {
149 // note: This method is not thread-safe
150 // need to ensure that the instance of AnFileDataManager has been locked before use
151 std::shared_ptr<AnFileInfo> info = std::make_shared<AnFileInfo>(AnFileInfo());
152 if (!info->Load(fileName, cb)) {
153 return false;
154 }
155 return UnsafeLoadFromAOTInternal(fileName, info);
156 }
157 #endif
158
UnSafeGetFileInfoIndex(const std::string & fileName)159 uint32_t AnFileDataManager::UnSafeGetFileInfoIndex(const std::string &fileName)
160 {
161 auto iter = anFileNameToIndexMap_.find(fileName);
162 if (iter == anFileNameToIndexMap_.end()) {
163 return INVALID_INDEX;
164 }
165 return anFileNameToIndexMap_.at(fileName);
166 }
167
SafeGetFileInfoIndex(const std::string & fileName)168 uint32_t AnFileDataManager::SafeGetFileInfoIndex(const std::string &fileName)
169 {
170 ReadLockHolder lock(lock_);
171 return UnSafeGetFileInfoIndex(fileName);
172 }
173
SafeGetAnFileInfo(uint32_t index)174 std::shared_ptr<AnFileInfo> AnFileDataManager::SafeGetAnFileInfo(uint32_t index)
175 {
176 ReadLockHolder lock(lock_);
177 return UnSafeGetAnFileInfo(index);
178 }
179
SafeGetStubFileInfo()180 std::shared_ptr<StubFileInfo> AnFileDataManager::SafeGetStubFileInfo()
181 {
182 ReadLockHolder lock(lock_);
183 return loadedStub_;
184 }
185
SafeMergeChecksumInfo(const std::unordered_map<CString,uint32_t> & fileNameToChecksumMap)186 void AnFileDataManager::SafeMergeChecksumInfo(const std::unordered_map<CString, uint32_t> &fileNameToChecksumMap)
187 {
188 WriteLockHolder lock(lock_);
189 UnsafeMergeChecksumInfo(fileNameToChecksumMap);
190 }
191
192 // this method is not thread safe,please make sure have lock
UnsafeMergeChecksumInfo(const std::unordered_map<CString,uint32_t> & fileNameToChecksumMap)193 void AnFileDataManager::UnsafeMergeChecksumInfo(const std::unordered_map<CString, uint32_t> &fileNameToChecksumMap)
194 {
195 if (fullFileNameToChecksumMap_.empty()) {
196 fullFileNameToChecksumMap_ = fileNameToChecksumMap;
197 return;
198 }
199 for (const auto &newPair : fileNameToChecksumMap) {
200 bool alreadyHasChecksum = false;
201 for (auto &fullPair : fullFileNameToChecksumMap_) {
202 if (fullPair.first == newPair.first) {
203 alreadyHasChecksum = true;
204 VerifyChecksumOrSetToInvalid(newPair, fullPair);
205 break;
206 }
207 }
208 if (!alreadyHasChecksum) {
209 fullFileNameToChecksumMap_.emplace(newPair.first, newPair.second);
210 }
211 }
212 }
213
VerifyChecksumOrSetToInvalid(const std::pair<CString,uint32_t> & newPair,std::pair<const CString,uint32_t> & fullPair)214 void AnFileDataManager::VerifyChecksumOrSetToInvalid(const std::pair<CString, uint32_t> &newPair,
215 std::pair<const CString, uint32_t> &fullPair)
216 {
217 if (fullPair.second == static_cast<uint32_t>(-1)) {
218 LOG_ECMA(ERROR) << "MergeChecksumInfo fail because checksum is INVALID. "
219 << " file name: " << newPair.first;
220 return;
221 }
222 if (newPair.second != fullPair.second) {
223 LOG_ECMA(ERROR) << "MergeChecksumInfo fail because of different checksum, set to INVALID. "
224 << " file name: " << newPair.first << "old checksum: " << fullPair.second
225 << " new checksum: " << newPair.second;
226 fullPair.second = static_cast<uint32_t>(-1);
227 }
228 }
229
SafeGetfullFileNameToChecksumMap()230 const std::unordered_map<CString, uint32_t> &AnFileDataManager::SafeGetfullFileNameToChecksumMap()
231 {
232 ReadLockHolder lock(lock_);
233 return UnsafeGetfullFileNameToChecksumMap();
234 }
235
UnsafeGetfullFileNameToChecksumMap() const236 const std::unordered_map<CString, uint32_t> &AnFileDataManager::UnsafeGetfullFileNameToChecksumMap() const
237 {
238 return fullFileNameToChecksumMap_;
239 }
240
SafeCheckFilenameToChecksum(const CString & fileName,uint32_t checksum)241 bool AnFileDataManager::SafeCheckFilenameToChecksum(const CString &fileName, uint32_t checksum)
242 {
243 ReadLockHolder lock(lock_);
244 return UnsafeCheckFilenameToChecksum(fileName, checksum);
245 }
246
UnsafeCheckFilenameToChecksum(const CString & fileName,uint32_t checksum)247 bool AnFileDataManager::UnsafeCheckFilenameToChecksum(const CString &fileName, uint32_t checksum)
248 {
249 for (const auto &fileNameToChecksumPair : fullFileNameToChecksumMap_) {
250 if (fileName == fileNameToChecksumPair.first) {
251 if (fileNameToChecksumPair.second == static_cast<uint32_t>(-1) ||
252 fileNameToChecksumPair.second != checksum) {
253 LOG_COMPILER(ERROR) << "an file verify checksum fail: checksum is invalid or different. FileName: "
254 << fileName << " old checksum: " << fileNameToChecksumPair.second
255 << " provided new checksum " << checksum;
256 return false;
257 } else {
258 return true;
259 }
260 }
261 }
262 return true;
263 }
264
265 // Using for cpuprofiler to check if the ReadLock can be held in signal handler, to avoid the reentrancy deadlock.
SafeTryReadLock()266 bool AnFileDataManager::SafeTryReadLock()
267 {
268 // Try to acquire the lock when the signal callback starts to execute. At this time, the vm thread is interrupted,
269 // so the status of whether the lock is held by vm thread will not change until the signal callback ends. If the
270 // attempt is successful, it means that the current vm thread does not hold the lock,the reentrancy problem will
271 // not occur in the callback.
272 if (lock_.TryReadLock()) {
273 lock_.Unlock();
274 return true;
275 }
276 return false;
277 }
278
SafeInsideStub(uintptr_t pc)279 bool AnFileDataManager::SafeInsideStub(uintptr_t pc)
280 {
281 ReadLockHolder lock(lock_);
282 if (loadedStub_ == nullptr) {
283 LOG_COMPILER(ERROR) << "SafeInsideStub: The stub file is not loaded.";
284 return false;
285 }
286
287 uint64_t stubStartAddr = loadedStub_->GetAsmStubAddr();
288 uint64_t stubEndAddr = stubStartAddr + loadedStub_->GetAsmStubSize();
289 if (pc >= stubStartAddr && pc <= stubEndAddr) {
290 return true;
291 }
292
293 const std::vector<ModuleSectionDes> &des = loadedStub_->GetCodeUnits();
294 for (const auto &curDes : des) {
295 if (curDes.ContainCode(pc)) {
296 return true;
297 }
298 }
299
300 return false;
301 }
302
SafeInsideAOT(uintptr_t pc)303 bool AnFileDataManager::SafeInsideAOT(uintptr_t pc)
304 {
305 ReadLockHolder lock(lock_);
306 for (auto &info : loadedAn_) {
307 const std::vector<ModuleSectionDes> &des = info->GetCodeUnits();
308 for (const auto &curDes : des) {
309 if (curDes.ContainCode(pc)) {
310 return true;
311 }
312 }
313 }
314 return false;
315 }
316
SafeCalCallSiteInfo(uintptr_t retAddr,bool isDeopt)317 AOTFileInfo::CallSiteInfo AnFileDataManager::SafeCalCallSiteInfo(uintptr_t retAddr, bool isDeopt)
318 {
319 ReadLockHolder lock(lock_);
320 AOTFileInfo::CallSiteInfo callsiteInfo;
321
322 bool ans = false;
323 if (loadedStub_ != nullptr) {
324 ans = loadedStub_->CalCallSiteInfo(retAddr, callsiteInfo, true, isDeopt);
325 }
326 if (ans) {
327 return callsiteInfo;
328 }
329 // aot
330 for (auto &info : loadedAn_) {
331 ans = info->CalCallSiteInfo(retAddr, callsiteInfo, false, isDeopt);
332 if (ans) {
333 return callsiteInfo;
334 }
335 }
336 return callsiteInfo;
337 }
338 } // namespace panda::ecmascript
339