• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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 "runtime/mem/refstorage/reference_storage.h"
17 
18 #include "libpandabase/mem/mem.h"
19 #include "libpandabase/utils/dfx.h"
20 #include "runtime/include/thread.h"
21 #include "runtime/mem/object_helpers.h"
22 #include "runtime/mem/refstorage/global_object_storage.h"
23 #include "runtime/mem/gc/gc_root.h"
24 #include "runtime/include/stack_walker-inl.h"
25 #include "libpandabase/utils/bit_utils.h"
26 
27 namespace ark::mem {
28 
29 // NOTE(alovkov): remove check for null, create managed thread in test instead of std::thread
30 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
31 #define ASSERT_THREAD_STATE() \
32     ASSERT(MTManagedThread::GetCurrent() == nullptr || !MTManagedThread::GetCurrent()->IsInNativeCode())
33 
ReferenceStorage(GlobalObjectStorage * globalStorage,mem::InternalAllocatorPtr allocator,bool refCheckValidate)34 ReferenceStorage::ReferenceStorage(GlobalObjectStorage *globalStorage, mem::InternalAllocatorPtr allocator,
35                                    bool refCheckValidate)
36     : globalStorage_(globalStorage), internalAllocator_(allocator), refCheckValidate_(refCheckValidate)
37 {
38 }
39 
~ReferenceStorage()40 ReferenceStorage::~ReferenceStorage()
41 {
42     if (frameAllocator_ != nullptr) {
43         internalAllocator_->Delete(frameAllocator_);
44     }
45     if (localStorage_ != nullptr) {
46         internalAllocator_->Delete(localStorage_);
47     }
48 }
49 
Init()50 bool ReferenceStorage::Init()
51 {
52     if (localStorage_ != nullptr || frameAllocator_ != nullptr || blocksCount_ != 0) {
53         return false;
54     }
55     localStorage_ = internalAllocator_->New<PandaVector<RefBlock *>>(internalAllocator_->Adapter());
56     if (localStorage_ == nullptr) {
57         return false;
58     }
59     frameAllocator_ = internalAllocator_->New<StorageFrameAllocator>();
60     if (frameAllocator_ == nullptr) {
61         return false;
62     }
63     // main frame should always be created
64     auto *firstBlock = CreateBlock();
65     if (firstBlock == nullptr) {
66         return false;
67     }
68     firstBlock->Reset();
69     blocksCount_ = 1;
70     localStorage_->push_back(firstBlock);
71     return true;
72 }
73 
IsValidRef(const Reference * ref)74 bool ReferenceStorage::IsValidRef(const Reference *ref)
75 {
76     ASSERT(ref != nullptr);
77     auto type = Reference::GetType(ref);
78 
79     bool res = false;
80     if (type == mem::Reference::ObjectType::STACK) {
81         res = StackReferenceCheck(ref);
82     } else if (type == mem::Reference::ObjectType::GLOBAL || type == mem::Reference::ObjectType::WEAK) {
83         // global-storage should accept ref with type
84         res = globalStorage_->IsValidGlobalRef(ref);
85     } else {
86         auto refWithoutType = Reference::GetRefWithoutType(ref);
87         // NOTE(alovkov): can be optimized with mmap + make additional checks that we really have ref in slots,
88         // Issue 3645
89         res = frameAllocator_->Contains(reinterpret_cast<void *>(refWithoutType));
90     }
91     return res;
92 }
93 
GetObjectType(const Reference * ref)94 Reference::ObjectType ReferenceStorage::GetObjectType(const Reference *ref)
95 {
96     return ref->GetType();
97 }
98 
99 // NOLINTNEXTLINE(readability-function-size)
NewRef(const ObjectHeader * object,Reference::ObjectType type)100 Reference *ReferenceStorage::NewRef(const ObjectHeader *object, Reference::ObjectType type)
101 {
102     ASSERT(type != Reference::ObjectType::STACK);
103     ASSERT_THREAD_STATE();
104     if (object == nullptr) {
105         return nullptr;
106     }
107     ValidateObject(nullptr, object);
108     Reference *ref = nullptr;
109     if (type == Reference::ObjectType::GLOBAL || type == Reference::ObjectType::WEAK) {
110         ref = static_cast<Reference *>(globalStorage_->Add(object, type));
111     } else {
112         auto *lastBlock = localStorage_->back();
113         ASSERT(lastBlock != nullptr);
114 
115         RefBlock *curBlock = nullptr;
116         if (lastBlock->IsFull()) {
117             curBlock = CreateBlock();
118             if (curBlock == nullptr) {
119                 // NOTE(alovkov): make free-list of holes in all blocks. O(n) operations, but it will be done only once
120                 LOG(ERROR, GC) << "Can't allocate local ref for object: " << object
121                                << ", cls: " << object->ClassAddr<ark::Class>()->GetName()
122                                << " with type: " << static_cast<int>(type);
123                 DumpLocalRef();
124                 return nullptr;
125             }
126             curBlock->Reset(lastBlock);
127             (*localStorage_)[localStorage_->size() - 1] = curBlock;
128         } else {
129             curBlock = lastBlock;
130         }
131         ref = curBlock->AddRef(object, type);
132     }
133     LOG(DEBUG, GC) << "Add reference to object: " << object << " type: " << static_cast<int>(type) << " ref: " << ref;
134     return ref;
135 }
136 
RemoveRef(const Reference * ref)137 void ReferenceStorage::RemoveRef(const Reference *ref)
138 {
139     if (ref == nullptr) {
140         return;
141     }
142 
143     if (refCheckValidate_) {
144         if (UNLIKELY(!IsValidRef(ref))) {
145             // Undefined behavior, we just print warning here.
146             LOG(WARNING, GC) << "Try to remove not existed ref: " << ref;
147             return;
148         }
149     }
150     Reference::ObjectType objectType = ref->GetType();
151     if (objectType == Reference::ObjectType::GLOBAL || objectType == Reference::ObjectType::WEAK) {
152         // When the global or weak global ref is created by another thread, we can't suppose current thread is in
153         // MANAGED_CODE state.
154         LOG(DEBUG, GC) << "Remove global reference: " << ref << " obj: " << globalStorage_->Get(ref);
155         globalStorage_->Remove(ref);
156     } else if (objectType == Reference::ObjectType::LOCAL) {
157         ASSERT_THREAD_STATE();
158         auto addr = ToUintPtr(ref);
159         auto blockAddr = (addr >> BLOCK_ALIGNMENT) << BLOCK_ALIGNMENT;
160         auto *block = reinterpret_cast<RefBlock *>(blockAddr);
161 
162         LOG(DEBUG, GC) << "Remove local reference: " << ref << " obj: " << FindLocalObject(ref);
163         block->Remove(ref);
164     } else if (objectType == Reference::ObjectType::STACK) {
165         LOG(ERROR, GC) << "Cannot remove stack type: " << ref;
166     } else {
167         LOG(FATAL, GC) << "Unknown reference type: " << ref;
168     }
169 }
170 
GetObject(const Reference * ref)171 ObjectHeader *ReferenceStorage::GetObject(const Reference *ref)
172 {
173     if (UNLIKELY(ref == nullptr)) {
174         return nullptr;
175     }
176 
177     if (refCheckValidate_) {
178         if (UNLIKELY(!IsValidRef(ref))) {
179             // Undefined behavior, we just print warning here.
180             LOG(WARNING, GC) << "Try to GetObject from a not existed ref: " << ref;
181             return nullptr;
182         }
183     }
184     Reference::ObjectType objectType = ref->GetType();
185     switch (objectType) {
186         case Reference::ObjectType::GLOBAL:
187         case Reference::ObjectType::WEAK: {
188             ObjectHeader *obj = globalStorage_->Get(ref);
189 #ifndef NDEBUG
190             // only weakly reachable objects can be null in storage
191             if (objectType == mem::Reference::ObjectType::GLOBAL) {
192                 ASSERT(obj != nullptr);
193             }
194 #endif
195             return obj;
196         }
197         case Reference::ObjectType::STACK: {
198             // In current scheme object passed in 64-bit argument
199             // But compiler may store 32-bit and trash in hi-part
200             // That's why need cut object pointer
201             return reinterpret_cast<ObjectHeader *>(
202                 (*reinterpret_cast<ObjectPointerType *>(Reference::GetRefWithoutType(ref))));
203         }
204         case Reference::ObjectType::LOCAL: {
205             ObjectHeader *obj = FindLocalObject(ref);
206             ASSERT(obj != nullptr);
207 #ifndef NDEBUG
208             /*
209              * classes are not movable objects, so they can be read from storage in native code, but general objects are
210              * not
211              */
212             auto baseCls = obj->ClassAddr<BaseClass>();
213             if (!baseCls->IsDynamicClass()) {
214                 auto cls = static_cast<Class *>(baseCls);
215                 if (!cls->IsClassClass()) {
216                     ASSERT_THREAD_STATE();
217                 }
218             }
219 #endif
220             return obj;
221         }
222         default: {
223             LOG(FATAL, RUNTIME) << "Unknown reference: " << ref << " type: " << static_cast<int>(objectType);
224         }
225     }
226     return nullptr;
227 }
228 
PushLocalFrame(uint32_t capacity)229 bool ReferenceStorage::PushLocalFrame(uint32_t capacity)
230 {
231     ASSERT_THREAD_STATE();
232     size_t needBlocks = (static_cast<uint64_t>(capacity) + RefBlock::REFS_IN_BLOCK - 1) / RefBlock::REFS_IN_BLOCK;
233 
234     size_t blocksFree = MAX_STORAGE_BLOCK_COUNT - blocksCount_;
235     if (needBlocks > blocksFree) {
236         LOG(ERROR, GC) << "Free size of local reference storage is less than capacity: " << capacity
237                        << " blocks_count_: " << blocksCount_ << " need_blocks: " << needBlocks
238                        << " blocks_free: " << blocksFree;
239         return false;
240     }
241     auto *newBlock = CreateBlock();
242     if (newBlock == nullptr) {
243         LOG(FATAL, GC) << "Can't allocate new frame";
244         UNREACHABLE();
245     }
246     newBlock->Reset();
247     localStorage_->push_back(newBlock);
248     return true;
249 }
250 
PopLocalFrame(const Reference * result)251 Reference *ReferenceStorage::PopLocalFrame(const Reference *result)
252 {
253     ASSERT_THREAD_STATE();
254 
255     ObjectHeader *obj;
256     if (result != nullptr) {
257         obj = GetObject(result);
258     } else {
259         obj = nullptr;
260     }
261 
262     if (cachedBlock_ != nullptr) {
263         RemoveBlock(cachedBlock_);
264         cachedBlock_ = nullptr;
265     }
266 
267     // We should add a log which refs are deleted under debug
268     auto *lastBlock = localStorage_->back();
269     auto isFirst = localStorage_->size() == 1;
270     while (lastBlock != nullptr) {
271         auto *prev = lastBlock->GetPrev();
272         if (prev == nullptr && isFirst) {
273             // it's the first block, which we don't delete
274             break;
275         }
276         // cache the last block for ping-pong effect
277         if (prev == nullptr) {
278             if (cachedBlock_ == nullptr) {
279                 cachedBlock_ = lastBlock;
280                 break;
281             }
282         }
283         RemoveBlock(lastBlock);
284         lastBlock = prev;
285     }
286 
287     if (obj == nullptr) {
288         localStorage_->pop_back();
289         return nullptr;
290     }
291 
292     Reference::ObjectType type = result->GetType();
293     localStorage_->pop_back();
294     return NewRef(obj, type);
295 }
296 
EnsureLocalCapacity(uint32_t capacity)297 bool ReferenceStorage::EnsureLocalCapacity(uint32_t capacity)
298 {
299     size_t needBlocks = (static_cast<uint64_t>(capacity) + RefBlock::REFS_IN_BLOCK - 1) / RefBlock::REFS_IN_BLOCK;
300     size_t blocksFreed = MAX_STORAGE_BLOCK_COUNT - blocksCount_;
301     if (needBlocks > blocksFreed) {
302         LOG(ERROR, GC) << "Can't store size: " << capacity << " in local references";
303         return false;
304     }
305     return true;
306 }
307 
FindLocalObject(const Reference * ref)308 ObjectHeader *ReferenceStorage::FindLocalObject(const Reference *ref)
309 {
310     ref = Reference::GetRefWithoutType(ref);
311     ObjectPointer<ObjectHeader> objPointer = *(reinterpret_cast<const ObjectPointer<ObjectHeader> *>(ref));
312     return objPointer;
313 }
314 
GetAllObjects()315 PandaVector<ObjectHeader *> ReferenceStorage::GetAllObjects()
316 {
317     auto objects = globalStorage_->GetAllObjects();
318     for (const auto &currentFrame : *localStorage_) {
319         auto lastBlock = currentFrame;
320         const PandaVector<mem::Reference *> &refs = lastBlock->GetAllReferencesInFrame();
321         for (const auto &ref : refs) {
322             ObjectHeader *obj = FindLocalObject(ref);
323             objects.push_back(reinterpret_cast<ObjectHeader *>(obj));
324         }
325     }
326     return objects;
327 }
328 
VisitObjects(const GCRootVisitor & gcRootVisitor,mem::RootType rootType)329 void ReferenceStorage::VisitObjects(const GCRootVisitor &gcRootVisitor, mem::RootType rootType)
330 {
331     for (const auto &frame : *localStorage_) {
332         frame->VisitObjects(gcRootVisitor, rootType);
333     }
334 }
335 
UpdateMovedRefs(const GCRootUpdater & gcRootUpdater)336 void ReferenceStorage::UpdateMovedRefs(const GCRootUpdater &gcRootUpdater)
337 {
338     for (const auto &frame : *localStorage_) {
339         frame->UpdateMovedRefs(gcRootUpdater);
340     }
341 }
342 
DumpLocalRefClasses()343 void ReferenceStorage::DumpLocalRefClasses()
344 {
345     PandaMap<PandaString, int> classesInfo;
346 
347     for (const auto &frame : *localStorage_) {
348         auto lastBlock = frame;
349         auto refs = lastBlock->GetAllReferencesInFrame();
350         for (const auto &ref : refs) {
351             ObjectHeader *obj = FindLocalObject(ref);
352             PandaString clsName = ConvertToString(obj->ClassAddr<ark::Class>()->GetName());
353             classesInfo[clsName]++;
354         }
355     }
356     using InfoPair = std::pair<PandaString, int>;
357     PandaVector<InfoPair> infoVec(classesInfo.begin(), classesInfo.end());
358     size_t size = std::min(MAX_DUMP_LOCAL_NUMS, infoVec.size());
359     std::partial_sort(infoVec.begin(), infoVec.begin() + size, infoVec.end(),
360                       [](const InfoPair &lhs, const InfoPair &rhs) { return lhs.second < rhs.second; });
361     LOG(ERROR, GC) << "The top " << size << " classes of local references are:";
362     for (size_t i = 0; i < size; i++) {
363         LOG(ERROR, GC) << "\t" << infoVec[i].first << ": " << infoVec[i].second;
364     }
365 }
366 
DumpLocalRef()367 void ReferenceStorage::DumpLocalRef()
368 {
369     if (DfxController::IsInitialized() && DfxController::GetOptionValue(DfxOptionHandler::REFERENCE_DUMP) != 1) {
370         return;
371     }
372     LOG(ERROR, GC) << "--- local reference storage dump ---";
373     LOG(ERROR, GC) << "Local reference storage addr: " << &localStorage_;
374     LOG(ERROR, GC) << "Dump the last several local references info(max " << MAX_DUMP_LOCAL_NUMS << "):";
375     size_t nDump = 0;
376 
377     for (auto it = localStorage_->rbegin(); it != localStorage_->rend(); ++it) {
378         auto *frame = *it;
379         auto refs = frame->GetAllReferencesInFrame();
380         for (const auto &ref : refs) {
381             ObjectHeader *res = FindLocalObject(ref);
382             PandaString clsName = ConvertToString(res->ClassAddr<ark::Class>()->GetName());
383             LOG(ERROR, GC) << "\t local reference: " << ref << ", object: " << res << ", cls: " << clsName;
384             nDump++;
385             if (nDump == MAX_DUMP_LOCAL_NUMS) {
386                 DumpLocalRefClasses();
387                 LOG(ERROR, GC) << "---";
388                 LOG(ERROR, GC) << "Storage dumped maximum number of references";
389                 return;
390             }
391         }
392     }
393 }
394 
CreateBlock()395 RefBlock *ReferenceStorage::CreateBlock()
396 {
397     if (blocksCount_ == MAX_STORAGE_BLOCK_COUNT) {
398         return nullptr;
399     }
400 
401     if (cachedBlock_ != nullptr) {
402         RefBlock *newBlock = cachedBlock_;
403         cachedBlock_ = nullptr;
404         return newBlock;
405     }
406 
407     blocksCount_++;
408     return static_cast<RefBlock *>(frameAllocator_->Alloc(BLOCK_SIZE));
409 }
410 
RemoveBlock(RefBlock * block)411 void ReferenceStorage::RemoveBlock(RefBlock *block)
412 {
413     frameAllocator_->Free(block);
414     blocksCount_--;
415 }
416 
RemoveAllLocalRefs()417 void ReferenceStorage::RemoveAllLocalRefs()
418 {
419     ASSERT_THREAD_STATE();
420 
421     for (const auto &frame : *localStorage_) {
422         auto lastBlock = frame;
423         auto refs = lastBlock->GetAllReferencesInFrame();
424         for (const auto &ref : refs) {
425             lastBlock->Remove(ref);
426         }
427     }
428 }
429 
GetGlobalObjectStorageSize()430 size_t ReferenceStorage::GetGlobalObjectStorageSize()
431 {
432     return globalStorage_->GetSize();
433 }
434 
GetLocalObjectStorageSize()435 size_t ReferenceStorage::GetLocalObjectStorageSize()
436 {
437     size_t size = 0;
438     for (const auto &block : *localStorage_) {
439         auto *currentBlock = block;
440         size += currentBlock->GetAllReferencesInFrame().size();
441     }
442     return size;
443 }
444 
SetRefCheckValidate(bool refCheckValidate)445 void ReferenceStorage::SetRefCheckValidate(bool refCheckValidate)
446 {
447     refCheckValidate_ = refCheckValidate;
448 }
449 
StackReferenceCheck(const Reference * stackRefInput)450 bool ReferenceStorage::StackReferenceCheck(const Reference *stackRefInput)
451 {
452     ASSERT(stackRefInput->IsStack());
453     ManagedThread *thread = ManagedThread::GetCurrent();
454     ASSERT(thread != nullptr);
455 
456     for (auto pframe = StackWalker::Create(thread); pframe.HasFrame(); pframe.NextFrame()) {
457         if (!pframe.IsCFrame()) {
458             return false;
459         }
460 
461         auto cframe = pframe.GetCFrame();
462         if (!cframe.IsNative()) {
463             return false;
464         }
465 
466         bool res = false;
467         pframe.IterateObjectsWithInfo([&cframe, &stackRefInput, &res](auto &regInfo, [[maybe_unused]] auto &vreg) {
468             auto slotTypeRef = cframe.GetValuePtrFromSlot(regInfo.GetValue());
469             auto objectHeader = bit_cast<ObjectHeader **, const ark::CFrame::SlotType *>(slotTypeRef);
470             auto stackRef = NewStackRef(objectHeader);
471             if (stackRef == stackRefInput) {
472                 res = true;
473                 return false;
474             }
475             return true;
476         });
477 
478         if (res) {
479             return true;
480         }
481     }
482     return false;
483 }
484 
485 }  // namespace ark::mem
486