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() const30 JSTaggedType GetObject() const 31 { 32 return obj_; 33 } 34 SetObject(JSTaggedType obj)35 void SetObject(JSTaggedType obj) 36 { 37 obj_ = obj; 38 } 39 GetNext() const40 Node *GetNext() const 41 { 42 return next_; 43 } 44 SetNext(Node *node)45 void SetNext(Node *node) 46 { 47 next_ = node; 48 } 49 GetPrev() const50 Node *GetPrev() const 51 { 52 return prev_; 53 } 54 SetPrev(Node *node)55 void SetPrev(Node *node) 56 { 57 prev_ = node; 58 } 59 GetIndex() const60 uint32_t GetIndex() const 61 { 62 return index_; 63 } 64 SetIndex(uint32_t index)65 void SetIndex(uint32_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() const80 bool IsUsing() const 81 { 82 return isUsing_; 83 } 84 IsWeak() const85 bool IsWeak() const 86 { 87 return isWeak_; 88 } 89 GetObjectAddress() const90 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([[maybe_unused]] 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 uint32_t index_ {0}; 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() const135 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() const179 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() const194 WeakClearCallback GetNativeFinalizeCallback() const 195 { 196 return nativeFinalizeCallback_; 197 } 198 GetFreeGlobalCallback() const199 WeakClearCallback GetFreeGlobalCallback() const 200 { 201 return freeGlobalCallback_; 202 } 203 CallFreeGlobalCallback()204 void CallFreeGlobalCallback() 205 { 206 if (freeGlobalCallback_ != nullptr) { 207 freeGlobalCallback_(reference_); 208 } 209 } 210 CallNativeFinalizeCallback()211 void CallNativeFinalizeCallback() 212 { 213 if (nativeFinalizeCallback_ != nullptr) { 214 nativeFinalizeCallback_(reference_); 215 } 216 } 217 private: 218 void *reference_ {nullptr}; 219 WeakClearCallback freeGlobalCallback_ {nullptr}; 220 WeakClearCallback nativeFinalizeCallback_ {nullptr}; 221 }; 222 223 template<typename T> 224 class NodeList { 225 public: NodeList()226 NodeList() 227 { 228 bool isWeak = std::is_same<T, WeakNode>::value; 229 for (uint32_t i = 0; i < GLOBAL_BLOCK_SIZE; i++) { 230 nodeList_[i].SetIndex(i); 231 nodeList_[i].SetWeak(isWeak); 232 } 233 } 234 ~NodeList() = default; 235 NodeToNodeList(T *node)236 inline static NodeList<T> *NodeToNodeList(T *node) 237 { 238 uintptr_t ptr = ToUintPtr(node) - node->GetIndex() * sizeof(T); 239 return reinterpret_cast<NodeList<T> *>(ptr); 240 } 241 NewNode(JSThread *thread, JSTaggedType value)242 inline T *NewNode(JSThread *thread, JSTaggedType value) 243 { 244 if (IsFull()) { 245 return nullptr; 246 } 247 T *node = &nodeList_[index_++]; 248 node->Reset(thread, usedList_, value, true); 249 if (usedList_ != nullptr) { 250 usedList_->SetPrev(node); 251 } 252 253 usedList_ = node; 254 return node; 255 } 256 GetFreeNode(JSThread *thread, JSTaggedType value)257 inline T *GetFreeNode(JSThread *thread, JSTaggedType value) 258 { 259 T *node = freeList_; 260 if (node != nullptr) { 261 freeList_ = reinterpret_cast<T *>(node->GetNext()); 262 node->Reset(thread, usedList_, value, true); 263 if (usedList_ != nullptr) { 264 usedList_->SetPrev(node); 265 } 266 usedList_ = node; 267 } 268 return node; 269 } 270 FreeNode(JSThread *thread, T *node)271 inline void FreeNode(JSThread *thread, T *node) 272 { 273 if (node->GetPrev() != nullptr) { 274 node->GetPrev()->SetNext(node->GetNext()); 275 } 276 if (node->GetNext() != nullptr) { 277 node->GetNext()->SetPrev(node->GetPrev()); 278 } 279 if (node == usedList_) { 280 usedList_ = reinterpret_cast<T *>(node->GetNext()); 281 } 282 node->Reset(thread, freeList_, JSTaggedValue::Undefined().GetRawData(), false); 283 if (node->IsWeak()) { 284 reinterpret_cast<WeakNode *>(node)->SetReference(nullptr); 285 reinterpret_cast<WeakNode *>(node)->SetFreeGlobalCallback(nullptr); 286 reinterpret_cast<WeakNode *>(node)->SetNativeFinalizeCallback(nullptr); 287 } 288 if (freeList_ != nullptr) { 289 freeList_->SetPrev(node); 290 } 291 freeList_ = node; 292 } 293 LinkTo(NodeList<T> *prev)294 inline void LinkTo(NodeList<T> *prev) 295 { 296 next_ = nullptr; 297 prev_ = prev; 298 prev_->next_ = this; 299 } RemoveList()300 inline void RemoveList() 301 { 302 if (next_ != nullptr) { 303 next_->SetPrev(prev_); 304 } 305 if (prev_ != nullptr) { 306 prev_->SetNext(next_); 307 } 308 if (freeNext_ != nullptr) { 309 freeNext_->SetFreePrev(freePrev_); 310 } 311 if (freePrev_ != nullptr) { 312 freePrev_->SetFreeNext(freeNext_); 313 } 314 } 315 IsFull()316 inline bool IsFull() 317 { 318 return index_ >= GLOBAL_BLOCK_SIZE; 319 } 320 HasFreeNode()321 inline bool HasFreeNode() 322 { 323 return freeList_ != nullptr; 324 } 325 HasUsagedNode()326 inline bool HasUsagedNode() 327 { 328 return !IsFull() || usedList_ != nullptr; 329 } 330 SetNext(NodeList *next)331 inline void SetNext(NodeList *next) 332 { 333 next_ = next; 334 } GetNext() const335 inline NodeList<T> *GetNext() const 336 { 337 return next_; 338 } 339 SetPrev(NodeList<T> *prev)340 inline void SetPrev(NodeList<T> *prev) 341 { 342 prev_ = prev; 343 } GetPrev() const344 inline NodeList<T> *GetPrev() const 345 { 346 return prev_; 347 } 348 SetFreeNext(NodeList<T> *next)349 inline void SetFreeNext(NodeList<T> *next) 350 { 351 freeNext_ = next; 352 } GetFreeNext() const353 inline NodeList<T> *GetFreeNext() const 354 { 355 return freeNext_; 356 } 357 SetFreePrev(NodeList<T> *prev)358 inline void SetFreePrev(NodeList<T> *prev) 359 { 360 freePrev_ = prev; 361 } GetFreePrev() const362 inline NodeList<T> *GetFreePrev() const 363 { 364 return freePrev_; 365 } 366 367 template<class Callback> IterateUsageGlobal(Callback callback)368 inline void IterateUsageGlobal(Callback callback) 369 { 370 T *next = usedList_; 371 T *current = nullptr; 372 while (next != nullptr) { 373 current = next; 374 next = reinterpret_cast<T *>(current->GetNext()); 375 ASSERT(current != next); 376 callback(current); 377 } 378 } 379 380 private: 381 static const uint32_t GLOBAL_BLOCK_SIZE = 256; 382 T nodeList_[GLOBAL_BLOCK_SIZE]; // all 383 T *freeList_ {nullptr}; // dispose node 384 T *usedList_ {nullptr}; // usage node 385 uint32_t index_ {0}; 386 NodeList<T> *next_ {nullptr}; 387 NodeList<T> *prev_ {nullptr}; 388 NodeList<T> *freeNext_ {nullptr}; 389 NodeList<T> *freePrev_ {nullptr}; 390 }; 391 392 template<typename T> 393 class EcmaGlobalStorage { 394 public: 395 using WeakClearCallback = void (*)(void *); 396 EcmaGlobalStorage(JSThread *thread, NativeAreaAllocator *allocator)397 EcmaGlobalStorage(JSThread *thread, NativeAreaAllocator *allocator) 398 : thread_(thread), allocator_(allocator) 399 { 400 topGlobalNodes_ = lastGlobalNodes_ = allocator_->New<NodeList<T>>(); 401 topWeakGlobalNodes_ = lastWeakGlobalNodes_ = allocator_->New<NodeList<WeakNode>>(); 402 } 403 ~EcmaGlobalStorage()404 ~EcmaGlobalStorage() 405 { 406 auto *weakNext = topWeakGlobalNodes_; 407 NodeList<WeakNode> *weakCurrent = nullptr; 408 while (weakNext != nullptr) { 409 weakCurrent = weakNext; 410 weakNext = weakCurrent->GetNext(); 411 weakCurrent->IterateUsageGlobal([] (WeakNode *node) { 412 node->SetUsing(false); 413 node->SetObject(JSTaggedValue::Undefined().GetRawData()); 414 node->CallFreeGlobalCallback(); 415 node->CallNativeFinalizeCallback(); 416 }); 417 allocator_->Delete(weakCurrent); 418 } 419 420 auto *next = topGlobalNodes_; 421 NodeList<T> *current = nullptr; 422 while (next != nullptr) { 423 current = next; 424 next = current->GetNext(); 425 current->IterateUsageGlobal([] (T *node) { 426 node->SetUsing(false); 427 node->SetObject(JSTaggedValue::Undefined().GetRawData()); 428 }); 429 allocator_->Delete(current); 430 } 431 } 432 NewGlobalHandle(JSTaggedType value)433 inline uintptr_t NewGlobalHandle(JSTaggedType value) 434 { 435 return NewGlobalHandleImplement(&lastGlobalNodes_, &freeListNodes_, value); 436 } 437 DisposeGlobalHandle(uintptr_t nodeAddr)438 inline void DisposeGlobalHandle(uintptr_t nodeAddr) 439 { 440 T *node = reinterpret_cast<T *>(nodeAddr); 441 if (!node->IsUsing()) { 442 return; 443 } 444 if (node->IsWeak()) { 445 DisposeGlobalHandleInner(reinterpret_cast<WeakNode *>(node), &weakFreeListNodes_, &topWeakGlobalNodes_, 446 &lastWeakGlobalNodes_); 447 } else { 448 DisposeGlobalHandleInner(node, &freeListNodes_, &topGlobalNodes_, &lastGlobalNodes_); 449 } 450 } 451 SetWeak(uintptr_t nodeAddr, void *ref = nullptr, WeakClearCallback freeGlobalCallBack = nullptr, WeakClearCallback nativeFinalizeCallBack = nullptr)452 inline uintptr_t SetWeak(uintptr_t nodeAddr, void *ref = nullptr, WeakClearCallback freeGlobalCallBack = nullptr, 453 WeakClearCallback nativeFinalizeCallBack = nullptr) 454 { 455 auto value = reinterpret_cast<T *>(nodeAddr)->GetObject(); 456 DisposeGlobalHandle(nodeAddr); 457 uintptr_t addr = NewGlobalHandleImplement(&lastWeakGlobalNodes_, &weakFreeListNodes_, value); 458 WeakNode *node = reinterpret_cast<WeakNode *>(addr); 459 node->SetReference(ref); 460 node->SetFreeGlobalCallback(freeGlobalCallBack); 461 node->SetNativeFinalizeCallback(nativeFinalizeCallBack); 462 return addr; 463 } 464 ClearWeak(uintptr_t nodeAddr)465 inline uintptr_t ClearWeak(uintptr_t nodeAddr) 466 { 467 auto value = reinterpret_cast<T *>(nodeAddr)->GetObject(); 468 DisposeGlobalHandle(nodeAddr); 469 uintptr_t ret = NewGlobalHandleImplement(&lastGlobalNodes_, &freeListNodes_, value); 470 return ret; 471 } IsWeak(uintptr_t addr) const472 inline bool IsWeak(uintptr_t addr) const 473 { 474 T *node = reinterpret_cast<T *>(addr); 475 return node->IsWeak(); 476 } 477 478 template<class Callback> IterateUsageGlobal(Callback callback)479 void IterateUsageGlobal(Callback callback) 480 { 481 NodeList<T> *next = topGlobalNodes_; 482 NodeList<T> *current = nullptr; 483 while (next != nullptr) { 484 current = next; 485 next = current->GetNext(); 486 ASSERT(current != next); 487 current->IterateUsageGlobal(callback); 488 } 489 } 490 491 template<class Callback> IterateWeakUsageGlobal(Callback callback)492 void IterateWeakUsageGlobal(Callback callback) 493 { 494 NodeList<WeakNode> *next = topWeakGlobalNodes_; 495 NodeList<WeakNode> *current = nullptr; 496 while (next != nullptr) { 497 current = next; 498 next = current->GetNext(); 499 ASSERT(current != next); 500 current->IterateUsageGlobal(callback); 501 } 502 } 503 504 private: 505 NO_COPY_SEMANTIC(EcmaGlobalStorage); 506 NO_MOVE_SEMANTIC(EcmaGlobalStorage); 507 508 template<typename S> DisposeGlobalHandleInner(S *node, NodeList<S> **freeList, NodeList<S> **topNodes, NodeList<S> **lastNodes)509 inline void DisposeGlobalHandleInner(S *node, NodeList<S> **freeList, NodeList<S> **topNodes, 510 NodeList<S> **lastNodes) 511 { 512 NodeList<S> *list = NodeList<S>::NodeToNodeList(node); 513 list->FreeNode(thread_, node); 514 515 // If NodeList has no usage node, then delete NodeList 516 if (!list->HasUsagedNode() && (*topNodes != *lastNodes)) { 517 list->RemoveList(); 518 if (*freeList == list) { 519 *freeList = list->GetFreeNext(); 520 } 521 if (*topNodes == list) { 522 *topNodes = list->GetNext(); 523 } 524 if (*lastNodes == list) { 525 *lastNodes = list->GetPrev(); 526 } 527 allocator_->Delete(list); 528 } else { 529 // Add to freeList 530 if (list != *freeList && list->GetFreeNext() == nullptr && list->GetFreePrev() == nullptr) { 531 list->SetFreeNext(*freeList); 532 if (*freeList != nullptr) { 533 (*freeList)->SetFreePrev(list); 534 } 535 *freeList = list; 536 } 537 } 538 } 539 540 template<typename S> NewGlobalHandleImplement(NodeList<S> **storage, NodeList<S> **freeList, JSTaggedType value)541 inline uintptr_t NewGlobalHandleImplement(NodeList<S> **storage, NodeList<S> **freeList, JSTaggedType value) 542 { 543 #if ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK 544 thread_->CheckJSTaggedType(value); 545 #endif 546 if (!(*storage)->IsFull()) { 547 // alloc new block 548 S *node = (*storage)->NewNode(thread_, value); 549 ASSERT(node != nullptr); 550 return node->GetObjectAddress(); 551 } 552 if (*freeList != nullptr) { 553 // use free_list node 554 S *node = (*freeList)->GetFreeNode(thread_, value); 555 ASSERT(node != nullptr); 556 if (!(*freeList)->HasFreeNode()) { 557 auto next = (*freeList)->GetFreeNext(); 558 (*freeList)->SetFreeNext(nullptr); 559 (*freeList)->SetFreePrev(nullptr); 560 if (next != nullptr) { 561 next->SetFreePrev(nullptr); 562 } 563 *freeList = next; 564 } 565 return node->GetObjectAddress(); 566 } 567 auto block = allocator_->New<NodeList<S>>(); 568 if (block == nullptr) { 569 LOG_ECMA(FATAL) << "NewGlobalHandleImplement:block is nullptr"; 570 } 571 block->LinkTo(*storage); 572 *storage = block; 573 574 // use node in block finally 575 S *node = (*storage)->NewNode(thread_, value); 576 ASSERT(node != nullptr); 577 return node->GetObjectAddress(); 578 } 579 580 [[maybe_unused]] JSThread *thread_ {nullptr}; 581 NativeAreaAllocator *allocator_ {nullptr}; 582 NodeList<T> *topGlobalNodes_ {nullptr}; 583 NodeList<T> *lastGlobalNodes_ {nullptr}; 584 NodeList<T> *freeListNodes_ {nullptr}; 585 586 NodeList<WeakNode> *topWeakGlobalNodes_ {nullptr}; 587 NodeList<WeakNode> *lastWeakGlobalNodes_ {nullptr}; 588 NodeList<WeakNode> *weakFreeListNodes_ {nullptr}; 589 }; 590 } // namespace panda::ecmascript 591 #endif // ECMASCRIPT_ECMA_GLOBAL_STORAGE_H 592