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 #ifndef ECMASCRIPT_ECMA_GLOBAL_STORAGE_H 17 #define ECMASCRIPT_ECMA_GLOBAL_STORAGE_H 18 19 #include "ecmascript/cross_vm/ecma_global_storage_hybrid.h" 20 21 #include "ecmascript/js_thread.h" 22 #include "ecmascript/mem/native_area_allocator.h" 23 #include "ecmascript/mem/c_containers.h" 24 #include "ecmascript/platform/backtrace.h" 25 #ifdef HOOK_ENABLE 26 #include "memory_trace.h" 27 #endif 28 29 namespace panda::ecmascript { 30 class Node { 31 public: GetObject()32 JSTaggedType GetObject() const 33 { 34 return obj_; 35 } 36 SetObject(JSTaggedType obj)37 void SetObject(JSTaggedType obj) 38 { 39 obj_ = obj; 40 } 41 GetNext()42 Node *GetNext() const 43 { 44 return next_; 45 } 46 SetNext(Node * node)47 void SetNext(Node *node) 48 { 49 next_ = node; 50 } 51 GetPrev()52 Node *GetPrev() const 53 { 54 return prev_; 55 } 56 SetPrev(Node * node)57 void SetPrev(Node *node) 58 { 59 prev_ = node; 60 } 61 GetIndex()62 uint32_t GetIndex() const 63 { 64 return index_; 65 } 66 SetIndex(uint32_t index)67 void SetIndex(uint32_t index) 68 { 69 index_ = index; 70 } 71 SetUsing(bool isUsing)72 void SetUsing(bool isUsing) 73 { 74 isUsing_ = isUsing; 75 } 76 SetWeak(bool isWeak)77 void SetWeak(bool isWeak) 78 { 79 isWeak_ = isWeak; 80 } 81 IsUsing()82 bool IsUsing() const 83 { 84 return isUsing_; 85 } 86 IsWeak()87 bool IsWeak() const 88 { 89 return isWeak_; 90 } 91 GetObjectAddress()92 uintptr_t GetObjectAddress() const 93 { 94 return reinterpret_cast<uintptr_t>(&obj_); 95 } 96 97 // If isUsing is true, it means that the node is being used, otherwise it means that node is be freed Reset(JSThread * thread,Node * next,JSTaggedType value,bool isUsing)98 void Reset([[maybe_unused]] JSThread *thread, Node *next, JSTaggedType value, bool isUsing) 99 { 100 SetPrev(nullptr); 101 SetNext(next); 102 SetObject(value); 103 SetUsing(isUsing); 104 #ifdef HOOK_ENABLE 105 memtrace((void *)next, sizeof(Node), "ArkJsGlobalHandle", isUsing); 106 #endif 107 } 108 109 private: 110 JSTaggedType obj_; 111 Node *next_ {nullptr}; 112 Node *prev_ {nullptr}; 113 uint32_t index_ {0}; 114 bool isUsing_ {false}; 115 bool isWeak_ {false}; 116 }; 117 118 class DebugNode : public Node { 119 public: Reset(JSThread * thread,Node * next,JSTaggedType value,bool isUsing)120 void Reset(JSThread *thread, Node *next, JSTaggedType value, bool isUsing) 121 { 122 Node::Reset(thread, next, value, isUsing); 123 ResetDebugInfo(); 124 if (isUsing && thread->IsStartGlobalLeakCheck()) { 125 if (JSTaggedValue(value).IsHeapObject()) { 126 if (thread->EnableGlobalObjectLeakCheck()) { 127 SaveBacktrace(thread, value); 128 } 129 } else { 130 if (thread->EnableGlobalPrimitiveLeakCheck()) { 131 SaveBacktrace(thread, value); 132 } 133 } 134 } 135 } 136 GetMarkCount()137 int32_t GetMarkCount() const 138 { 139 return markCount_; 140 } 141 MarkCount()142 void MarkCount() 143 { 144 markCount_++; 145 } 146 ResetDebugInfo()147 void ResetDebugInfo() 148 { 149 markCount_ = 0; 150 globalNumber_ = -1; 151 } 152 GetGlobalNumber()153 int32_t GetGlobalNumber() 154 { 155 return globalNumber_; 156 } 157 158 private: SaveBacktrace(JSThread * thread,JSTaggedType value)159 void SaveBacktrace(JSThread *thread, JSTaggedType value) 160 { 161 globalNumber_ = static_cast<int32_t>(thread->IncreaseGlobalNumberCount()); 162 std::ostringstream stack; 163 stack << "Global Handle Number:[" << globalNumber_ << "], value:0x" << 164 std::hex << value << std::endl; 165 Backtrace(stack, true); 166 thread->WriteToStackTraceFd(stack); 167 } 168 169 int32_t markCount_ {0}; 170 // A number generated in the order of distribution.It Used to help locate global memory leaks. 171 int32_t globalNumber_ {-1}; 172 }; 173 174 class WeakNode : public Node { 175 public: SetReference(void * ref)176 void SetReference(void *ref) 177 { 178 reference_ = ref; 179 } 180 GetReference()181 void* GetReference() const 182 { 183 return reference_; 184 } 185 SetFreeGlobalCallback(WeakClearCallback callback)186 void SetFreeGlobalCallback(WeakClearCallback callback) 187 { 188 freeGlobalCallback_ = callback; 189 } 190 SetNativeFinalizeCallback(WeakClearCallback callback)191 void SetNativeFinalizeCallback(WeakClearCallback callback) 192 { 193 nativeFinalizeCallback_ = callback; 194 } 195 GetNativeFinalizeCallback()196 WeakClearCallback GetNativeFinalizeCallback() const 197 { 198 return nativeFinalizeCallback_; 199 } 200 GetFreeGlobalCallback()201 WeakClearCallback GetFreeGlobalCallback() const 202 { 203 return freeGlobalCallback_; 204 } 205 CallFreeGlobalCallback()206 void CallFreeGlobalCallback() 207 { 208 if (freeGlobalCallback_ != nullptr) { 209 freeGlobalCallback_(reference_); 210 } 211 } 212 CallNativeFinalizeCallback()213 void CallNativeFinalizeCallback() 214 { 215 if (nativeFinalizeCallback_ != nullptr) { 216 nativeFinalizeCallback_(reference_); 217 } 218 } 219 private: 220 void *reference_ {nullptr}; 221 WeakClearCallback freeGlobalCallback_ {nullptr}; 222 WeakClearCallback nativeFinalizeCallback_ {nullptr}; 223 }; 224 225 template<typename T> 226 class NodeList { 227 public: NodeList()228 NodeList() 229 { 230 bool isWeak = std::is_same<T, WeakNode>::value; 231 for (uint32_t i = 0; i < GLOBAL_BLOCK_SIZE; i++) { 232 nodeList_[i].SetIndex(i); 233 nodeList_[i].SetWeak(isWeak); 234 } 235 } 236 ~NodeList() = default; 237 NodeToNodeList(T * node)238 inline static NodeList<T> *NodeToNodeList(T *node) 239 { 240 uintptr_t ptr = ToUintPtr(node) - node->GetIndex() * sizeof(T); 241 return reinterpret_cast<NodeList<T> *>(ptr); 242 } 243 NewNode(JSThread * thread,JSTaggedType value)244 inline T *NewNode(JSThread *thread, JSTaggedType value) 245 { 246 if (IsFull()) { 247 return nullptr; 248 } 249 T *node = &nodeList_[index_++]; 250 node->Reset(thread, usedList_, value, true); 251 if (usedList_ != nullptr) { 252 usedList_->SetPrev(node); 253 } 254 255 usedList_ = node; 256 return node; 257 } 258 GetFreeNode(JSThread * thread,JSTaggedType value)259 inline T *GetFreeNode(JSThread *thread, JSTaggedType value) 260 { 261 T *node = freeList_; 262 if (node != nullptr) { 263 freeList_ = reinterpret_cast<T *>(node->GetNext()); 264 node->Reset(thread, usedList_, value, true); 265 if (usedList_ != nullptr) { 266 usedList_->SetPrev(node); 267 } 268 usedList_ = node; 269 } 270 return node; 271 } 272 FreeNode(JSThread * thread,T * node)273 inline void FreeNode(JSThread *thread, T *node) 274 { 275 if (node->GetPrev() != nullptr) { 276 node->GetPrev()->SetNext(node->GetNext()); 277 } 278 if (node->GetNext() != nullptr) { 279 node->GetNext()->SetPrev(node->GetPrev()); 280 } 281 if (node == usedList_) { 282 usedList_ = reinterpret_cast<T *>(node->GetNext()); 283 } 284 node->Reset(thread, freeList_, JSTaggedValue::Undefined().GetRawData(), false); 285 if (node->IsWeak()) { 286 reinterpret_cast<WeakNode *>(node)->SetReference(nullptr); 287 reinterpret_cast<WeakNode *>(node)->SetFreeGlobalCallback(nullptr); 288 reinterpret_cast<WeakNode *>(node)->SetNativeFinalizeCallback(nullptr); 289 } 290 if (freeList_ != nullptr) { 291 freeList_->SetPrev(node); 292 } 293 freeList_ = node; 294 } 295 LinkTo(NodeList<T> * prev)296 inline void LinkTo(NodeList<T> *prev) 297 { 298 next_ = nullptr; 299 prev_ = prev; 300 prev_->next_ = this; 301 } RemoveList()302 inline void RemoveList() 303 { 304 if (next_ != nullptr) { 305 next_->SetPrev(prev_); 306 } 307 if (prev_ != nullptr) { 308 prev_->SetNext(next_); 309 } 310 if (freeNext_ != nullptr) { 311 freeNext_->SetFreePrev(freePrev_); 312 } 313 if (freePrev_ != nullptr) { 314 freePrev_->SetFreeNext(freeNext_); 315 } 316 } 317 IsFull()318 inline bool IsFull() 319 { 320 return index_ >= GLOBAL_BLOCK_SIZE; 321 } 322 HasFreeNode()323 inline bool HasFreeNode() 324 { 325 return freeList_ != nullptr; 326 } 327 HasUsagedNode()328 inline bool HasUsagedNode() 329 { 330 return !IsFull() || usedList_ != nullptr; 331 } 332 SetNext(NodeList * next)333 inline void SetNext(NodeList *next) 334 { 335 next_ = next; 336 } GetNext()337 inline NodeList<T> *GetNext() const 338 { 339 return next_; 340 } 341 SetPrev(NodeList<T> * prev)342 inline void SetPrev(NodeList<T> *prev) 343 { 344 prev_ = prev; 345 } GetPrev()346 inline NodeList<T> *GetPrev() const 347 { 348 return prev_; 349 } 350 SetFreeNext(NodeList<T> * next)351 inline void SetFreeNext(NodeList<T> *next) 352 { 353 freeNext_ = next; 354 } GetFreeNext()355 inline NodeList<T> *GetFreeNext() const 356 { 357 return freeNext_; 358 } 359 SetFreePrev(NodeList<T> * prev)360 inline void SetFreePrev(NodeList<T> *prev) 361 { 362 freePrev_ = prev; 363 } GetFreePrev()364 inline NodeList<T> *GetFreePrev() const 365 { 366 return freePrev_; 367 } 368 369 template<class Callback> IterateUsageGlobal(Callback callback)370 inline void IterateUsageGlobal(Callback callback) 371 { 372 T *next = usedList_; 373 T *current = nullptr; 374 while (next != nullptr) { 375 current = next; 376 next = reinterpret_cast<T *>(current->GetNext()); 377 ASSERT(current != next); 378 callback(current); 379 } 380 } 381 382 private: 383 static const uint32_t GLOBAL_BLOCK_SIZE = 256; 384 T nodeList_[GLOBAL_BLOCK_SIZE]; // all 385 T *freeList_ {nullptr}; // dispose node 386 T *usedList_ {nullptr}; // usage node 387 uint32_t index_ {0}; 388 NodeList<T> *next_ {nullptr}; 389 NodeList<T> *prev_ {nullptr}; 390 NodeList<T> *freeNext_ {nullptr}; 391 NodeList<T> *freePrev_ {nullptr}; 392 }; 393 394 enum class NodeKind : uint8_t { 395 NORMAL_NODE, 396 UNIFIED_NODE, 397 }; 398 399 template<typename T> 400 class EcmaGlobalStorage { 401 public: 402 using WeakClearCallback = void (*)(void *); 403 EcmaGlobalStorage(JSThread * thread,NativeAreaAllocator * allocator)404 EcmaGlobalStorage(JSThread *thread, NativeAreaAllocator *allocator) 405 : thread_(thread), allocator_(allocator) 406 { 407 topGlobalNodes_ = lastGlobalNodes_ = allocator_->New<NodeList<T>>(); 408 topXRefGlobalNodes_ = lastXRefGlobalNodes_ = allocator_->New<NodeList<T>>(); 409 topWeakGlobalNodes_ = lastWeakGlobalNodes_ = allocator_->New<NodeList<WeakNode>>(); 410 } 411 ~EcmaGlobalStorage()412 ~EcmaGlobalStorage() 413 { 414 auto clearWeakNodeCallback = [] (WeakNode *node) { 415 node->SetUsing(false); 416 node->SetObject(JSTaggedValue::Undefined().GetRawData()); 417 node->CallFreeGlobalCallback(); 418 node->CallNativeFinalizeCallback(); 419 }; 420 auto clearNodeCallback = [] (T *node) { 421 node->SetUsing(false); 422 node->SetObject(JSTaggedValue::Undefined().GetRawData()); 423 }; 424 425 IterateNodeList<decltype(clearWeakNodeCallback), WeakNode>(clearWeakNodeCallback, topWeakGlobalNodes_); 426 IterateNodeList<decltype(clearNodeCallback), T>(clearNodeCallback, topXRefGlobalNodes_); 427 IterateNodeList<decltype(clearNodeCallback), T>(clearNodeCallback, topGlobalNodes_); 428 } 429 430 template<class Callback, class S> IterateNodeList(Callback callback,NodeList<S> * nodeList)431 inline void IterateNodeList(Callback callback, NodeList<S> *nodeList) 432 { 433 auto *next = nodeList; 434 NodeList<S> *current = nullptr; 435 while (next != nullptr) { 436 current = next; 437 next = current->GetNext(); 438 ASSERT(current != next); 439 current->IterateUsageGlobal(callback); 440 } 441 } 442 443 template<NodeKind nodeKind> NewGlobalHandle(JSTaggedType value)444 inline uintptr_t NewGlobalHandle(JSTaggedType value) 445 { 446 if constexpr(nodeKind == NodeKind::NORMAL_NODE) { 447 return NewGlobalHandleImplement(&lastGlobalNodes_, &freeListNodes_, value); 448 } else if constexpr(nodeKind == NodeKind::UNIFIED_NODE) { 449 return NewGlobalHandleImplement(&lastXRefGlobalNodes_, &xRefFreeListNodes_, value); 450 } 451 } 452 453 template<NodeKind nodeKind> DisposeGlobalHandle(uintptr_t nodeAddr)454 inline void DisposeGlobalHandle(uintptr_t nodeAddr) 455 { 456 T *node = reinterpret_cast<T *>(nodeAddr); 457 if (!node->IsUsing()) { 458 return; 459 } 460 if constexpr(nodeKind == NodeKind::UNIFIED_NODE) { 461 DisposeGlobalHandleInner(node, &xRefFreeListNodes_, &topXRefGlobalNodes_, 462 &lastXRefGlobalNodes_); 463 } else if constexpr(nodeKind == NodeKind::NORMAL_NODE) { 464 if (node->IsWeak()) { 465 DisposeGlobalHandleInner(reinterpret_cast<WeakNode *>(node), &weakFreeListNodes_, &topWeakGlobalNodes_, 466 &lastWeakGlobalNodes_); 467 } else { 468 DisposeGlobalHandleInner(node, &freeListNodes_, &topGlobalNodes_, &lastGlobalNodes_); 469 } 470 } 471 } 472 473 inline uintptr_t SetWeak(uintptr_t nodeAddr, void *ref = nullptr, WeakClearCallback freeGlobalCallback = nullptr, 474 WeakClearCallback nativeFinalizeCallback = nullptr) 475 { 476 auto value = reinterpret_cast<T *>(nodeAddr)->GetObject(); 477 DisposeGlobalHandle<NodeKind::NORMAL_NODE>(nodeAddr); 478 uintptr_t addr = NewGlobalHandleImplement(&lastWeakGlobalNodes_, &weakFreeListNodes_, value); 479 WeakNode *node = reinterpret_cast<WeakNode *>(addr); 480 node->SetReference(ref); 481 node->SetFreeGlobalCallback(freeGlobalCallback); 482 node->SetNativeFinalizeCallback(nativeFinalizeCallback); 483 return addr; 484 } 485 ClearWeak(uintptr_t nodeAddr)486 inline uintptr_t ClearWeak(uintptr_t nodeAddr) 487 { 488 auto value = reinterpret_cast<T *>(nodeAddr)->GetObject(); 489 DisposeGlobalHandle<NodeKind::NORMAL_NODE>(nodeAddr); 490 uintptr_t ret = NewGlobalHandleImplement(&lastGlobalNodes_, &freeListNodes_, value); 491 return ret; 492 } 493 IsWeak(uintptr_t addr)494 inline bool IsWeak(uintptr_t addr) const 495 { 496 T *node = reinterpret_cast<T *>(addr); 497 return node->IsWeak(); 498 } 499 500 template<class Callback> IterateUsageGlobal(Callback && callback)501 void IterateUsageGlobal(Callback &&callback) 502 { 503 IterateNodeList<Callback, T>(callback, topGlobalNodes_); 504 if (nodeKind_ == NodeKind::UNIFIED_NODE) { 505 return; 506 } 507 IterateNodeList<Callback, T>(callback, topXRefGlobalNodes_); 508 } 509 510 template<class Callback> IterateWeakUsageGlobal(Callback callback)511 void IterateWeakUsageGlobal(Callback callback) 512 { 513 IterateNodeList<Callback, WeakNode>(callback, topWeakGlobalNodes_); 514 } 515 516 ECMAGLOBALSTORAGE_PUBLIC_HYBRID_EXTENSION() 517 private: 518 NO_COPY_SEMANTIC(EcmaGlobalStorage); 519 NO_MOVE_SEMANTIC(EcmaGlobalStorage); 520 template<typename S> DisposeGlobalHandleInner(S * node,NodeList<S> ** freeList,NodeList<S> ** topNodes,NodeList<S> ** lastNodes)521 inline void DisposeGlobalHandleInner(S *node, NodeList<S> **freeList, NodeList<S> **topNodes, 522 NodeList<S> **lastNodes) 523 { 524 NodeList<S> *list = NodeList<S>::NodeToNodeList(node); 525 list->FreeNode(thread_, node); 526 527 // If NodeList has no usage node, then delete NodeList 528 if (!list->HasUsagedNode() && (*topNodes != *lastNodes)) { 529 list->RemoveList(); 530 if (*freeList == list) { 531 *freeList = list->GetFreeNext(); 532 } 533 if (*topNodes == list) { 534 *topNodes = list->GetNext(); 535 } 536 if (*lastNodes == list) { 537 *lastNodes = list->GetPrev(); 538 } 539 allocator_->Delete(list); 540 } else { 541 // Add to freeList 542 if (list != *freeList && list->GetFreeNext() == nullptr && list->GetFreePrev() == nullptr) { 543 list->SetFreeNext(*freeList); 544 if (*freeList != nullptr) { 545 (*freeList)->SetFreePrev(list); 546 } 547 *freeList = list; 548 } 549 } 550 } 551 552 template<typename S> NewGlobalHandleImplement(NodeList<S> ** storage,NodeList<S> ** freeList,JSTaggedType value)553 inline uintptr_t NewGlobalHandleImplement(NodeList<S> **storage, NodeList<S> **freeList, JSTaggedType value) 554 { 555 #if ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK 556 thread_->CheckJSTaggedType(value); 557 #endif 558 if (!(*storage)->IsFull()) { 559 // alloc new block 560 S *node = (*storage)->NewNode(thread_, value); 561 ASSERT(node != nullptr); 562 return node->GetObjectAddress(); 563 } 564 if (*freeList != nullptr) { 565 // use free_list node 566 S *node = (*freeList)->GetFreeNode(thread_, value); 567 ASSERT(node != nullptr); 568 if (!(*freeList)->HasFreeNode()) { 569 auto next = (*freeList)->GetFreeNext(); 570 (*freeList)->SetFreeNext(nullptr); 571 (*freeList)->SetFreePrev(nullptr); 572 if (next != nullptr) { 573 next->SetFreePrev(nullptr); 574 } 575 *freeList = next; 576 } 577 return node->GetObjectAddress(); 578 } 579 auto block = allocator_->New<NodeList<S>>(); 580 if (block == nullptr) { 581 LOG_ECMA(FATAL) << "NewGlobalHandleImplement:block is nullptr"; 582 } 583 block->LinkTo(*storage); 584 *storage = block; 585 586 // use node in block finally 587 S *node = (*storage)->NewNode(thread_, value); 588 ASSERT(node != nullptr); 589 return node->GetObjectAddress(); 590 } 591 592 [[maybe_unused]] JSThread *thread_ {nullptr}; 593 NativeAreaAllocator *allocator_ {nullptr}; 594 NodeList<T> *topGlobalNodes_ {nullptr}; 595 NodeList<T> *lastGlobalNodes_ {nullptr}; 596 NodeList<T> *freeListNodes_ {nullptr}; 597 598 NodeList<WeakNode> *topWeakGlobalNodes_ {nullptr}; 599 NodeList<WeakNode> *lastWeakGlobalNodes_ {nullptr}; 600 NodeList<WeakNode> *weakFreeListNodes_ {nullptr}; 601 ECMAGLOBALSTORAGE_PRIVATE_HYBRID_EXTENSION(); 602 }; 603 } // namespace panda::ecmascript 604 #endif // ECMASCRIPT_ECMA_GLOBAL_STORAGE_H 605