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