• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 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 = (capacity + RefBlock::REFS_IN_BLOCK - 1) / RefBlock::REFS_IN_BLOCK;
233     size_t blocksFree = MAX_STORAGE_BLOCK_COUNT - blocksCount_;
234     if (needBlocks > blocksFree) {
235         LOG(ERROR, GC) << "Free size of local reference storage is less than capacity: " << capacity
236                        << " blocks_count_: " << blocksCount_ << " need_blocks: " << needBlocks
237                        << " blocks_free: " << blocksFree;
238         return false;
239     }
240     auto *newBlock = CreateBlock();
241     if (newBlock == nullptr) {
242         LOG(FATAL, GC) << "Can't allocate new frame";
243         UNREACHABLE();
244     }
245     newBlock->Reset();
246     localStorage_->push_back(newBlock);
247     return true;
248 }
249 
PopLocalFrame(const Reference * result)250 Reference *ReferenceStorage::PopLocalFrame(const Reference *result)
251 {
252     ASSERT_THREAD_STATE();
253 
254     ObjectHeader *obj;
255     if (result != nullptr) {
256         obj = GetObject(result);
257     } else {
258         obj = nullptr;
259     }
260 
261     if (cachedBlock_ != nullptr) {
262         RemoveBlock(cachedBlock_);
263         cachedBlock_ = nullptr;
264     }
265 
266     // We should add a log which refs are deleted under debug
267     auto *lastBlock = localStorage_->back();
268     auto isFirst = localStorage_->size() == 1;
269     while (lastBlock != nullptr) {
270         auto *prev = lastBlock->GetPrev();
271         if (prev == nullptr && isFirst) {
272             // it's the first block, which we don't delete
273             break;
274         }
275         // cache the last block for ping-pong effect
276         if (prev == nullptr) {
277             if (cachedBlock_ == nullptr) {
278                 cachedBlock_ = lastBlock;
279                 break;
280             }
281         }
282         RemoveBlock(lastBlock);
283         lastBlock = prev;
284     }
285 
286     if (obj == nullptr) {
287         localStorage_->pop_back();
288         return nullptr;
289     }
290 
291     Reference::ObjectType type = result->GetType();
292     localStorage_->pop_back();
293     return NewRef(obj, type);
294 }
295 
EnsureLocalCapacity(size_t capacity)296 bool ReferenceStorage::EnsureLocalCapacity(size_t capacity)
297 {
298     size_t needBlocks = (capacity + RefBlock::REFS_IN_BLOCK - 1) / RefBlock::REFS_IN_BLOCK;
299     size_t blocksFreed = MAX_STORAGE_BLOCK_COUNT - blocksCount_;
300     if (needBlocks > blocksFreed) {
301         LOG(ERROR, GC) << "Can't store size: " << capacity << " in local references";
302         return false;
303     }
304     return true;
305 }
306 
FindLocalObject(const Reference * ref)307 ObjectHeader *ReferenceStorage::FindLocalObject(const Reference *ref)
308 {
309     ref = Reference::GetRefWithoutType(ref);
310     ObjectPointer<ObjectHeader> objPointer = *(reinterpret_cast<const ObjectPointer<ObjectHeader> *>(ref));
311     return objPointer;
312 }
313 
GetAllObjects()314 PandaVector<ObjectHeader *> ReferenceStorage::GetAllObjects()
315 {
316     auto objects = globalStorage_->GetAllObjects();
317     for (const auto &currentFrame : *localStorage_) {
318         auto lastBlock = currentFrame;
319         const PandaVector<mem::Reference *> &refs = lastBlock->GetAllReferencesInFrame();
320         for (const auto &ref : refs) {
321             ObjectHeader *obj = FindLocalObject(ref);
322             objects.push_back(reinterpret_cast<ObjectHeader *>(obj));
323         }
324     }
325     return objects;
326 }
327 
VisitObjects(const GCRootVisitor & gcRootVisitor,mem::RootType rootType)328 void ReferenceStorage::VisitObjects(const GCRootVisitor &gcRootVisitor, mem::RootType rootType)
329 {
330     for (const auto &frame : *localStorage_) {
331         frame->VisitObjects(gcRootVisitor, rootType);
332     }
333 }
334 
UpdateMovedRefs()335 void ReferenceStorage::UpdateMovedRefs()
336 {
337     for (const auto &frame : *localStorage_) {
338         frame->UpdateMovedRefs();
339     }
340 }
341 
DumpLocalRefClasses()342 void ReferenceStorage::DumpLocalRefClasses()
343 {
344     PandaMap<PandaString, int> classesInfo;
345 
346     for (const auto &frame : *localStorage_) {
347         auto lastBlock = frame;
348         auto refs = lastBlock->GetAllReferencesInFrame();
349         for (const auto &ref : refs) {
350             ObjectHeader *obj = FindLocalObject(ref);
351             PandaString clsName = ConvertToString(obj->ClassAddr<ark::Class>()->GetName());
352             classesInfo[clsName]++;
353         }
354     }
355     using InfoPair = std::pair<PandaString, int>;
356     PandaVector<InfoPair> infoVec(classesInfo.begin(), classesInfo.end());
357     size_t size = std::min(MAX_DUMP_LOCAL_NUMS, infoVec.size());
358     std::partial_sort(infoVec.begin(), infoVec.begin() + size, infoVec.end(),
359                       [](const InfoPair &lhs, const InfoPair &rhs) { return lhs.second < rhs.second; });
360     LOG(ERROR, GC) << "The top " << size << " classes of local references are:";
361     for (size_t i = 0; i < size; i++) {
362         LOG(ERROR, GC) << "\t" << infoVec[i].first << ": " << infoVec[i].second;
363     }
364 }
365 
DumpLocalRef()366 void ReferenceStorage::DumpLocalRef()
367 {
368     if (DfxController::IsInitialized() && DfxController::GetOptionValue(DfxOptionHandler::REFERENCE_DUMP) != 1) {
369         return;
370     }
371     LOG(ERROR, GC) << "--- local reference storage dump ---";
372     LOG(ERROR, GC) << "Local reference storage addr: " << &localStorage_;
373     LOG(ERROR, GC) << "Dump the last several local references info(max " << MAX_DUMP_LOCAL_NUMS << "):";
374     size_t nDump = 0;
375 
376     for (auto it = localStorage_->rbegin(); it != localStorage_->rend(); ++it) {
377         auto *frame = *it;
378         auto refs = frame->GetAllReferencesInFrame();
379         for (const auto &ref : refs) {
380             ObjectHeader *res = FindLocalObject(ref);
381             PandaString clsName = ConvertToString(res->ClassAddr<ark::Class>()->GetName());
382             LOG(ERROR, GC) << "\t local reference: " << ref << ", object: " << res << ", cls: " << clsName;
383             nDump++;
384             if (nDump == MAX_DUMP_LOCAL_NUMS) {
385                 DumpLocalRefClasses();
386                 LOG(ERROR, GC) << "---";
387                 LOG(ERROR, GC) << "Storage dumped maximum number of references";
388                 return;
389             }
390         }
391     }
392 }
393 
CreateBlock()394 RefBlock *ReferenceStorage::CreateBlock()
395 {
396     if (blocksCount_ == MAX_STORAGE_BLOCK_COUNT) {
397         return nullptr;
398     }
399 
400     if (cachedBlock_ != nullptr) {
401         RefBlock *newBlock = cachedBlock_;
402         cachedBlock_ = nullptr;
403         return newBlock;
404     }
405 
406     blocksCount_++;
407     return static_cast<RefBlock *>(frameAllocator_->Alloc(BLOCK_SIZE));
408 }
409 
RemoveBlock(RefBlock * block)410 void ReferenceStorage::RemoveBlock(RefBlock *block)
411 {
412     frameAllocator_->Free(block);
413     blocksCount_--;
414 }
415 
RemoveAllLocalRefs()416 void ReferenceStorage::RemoveAllLocalRefs()
417 {
418     ASSERT_THREAD_STATE();
419 
420     for (const auto &frame : *localStorage_) {
421         auto lastBlock = frame;
422         auto refs = lastBlock->GetAllReferencesInFrame();
423         for (const auto &ref : refs) {
424             lastBlock->Remove(ref);
425         }
426     }
427 }
428 
GetGlobalObjectStorageSize()429 size_t ReferenceStorage::GetGlobalObjectStorageSize()
430 {
431     return globalStorage_->GetSize();
432 }
433 
GetLocalObjectStorageSize()434 size_t ReferenceStorage::GetLocalObjectStorageSize()
435 {
436     size_t size = 0;
437     for (const auto &block : *localStorage_) {
438         auto *currentBlock = block;
439         size += currentBlock->GetAllReferencesInFrame().size();
440     }
441     return size;
442 }
443 
SetRefCheckValidate(bool refCheckValidate)444 void ReferenceStorage::SetRefCheckValidate(bool refCheckValidate)
445 {
446     refCheckValidate_ = refCheckValidate;
447 }
448 
StackReferenceCheck(const Reference * stackRefInput)449 bool ReferenceStorage::StackReferenceCheck(const Reference *stackRefInput)
450 {
451     ASSERT(stackRefInput->IsStack());
452     ManagedThread *thread = ManagedThread::GetCurrent();
453     ASSERT(thread != nullptr);
454 
455     for (auto pframe = StackWalker::Create(thread); pframe.HasFrame(); pframe.NextFrame()) {
456         if (!pframe.IsCFrame()) {
457             return false;
458         }
459 
460         auto cframe = pframe.GetCFrame();
461         if (!cframe.IsNative()) {
462             return false;
463         }
464 
465         bool res = false;
466         pframe.IterateObjectsWithInfo([&cframe, &stackRefInput, &res](auto &regInfo, [[maybe_unused]] auto &vreg) {
467             auto slotTypeRef = cframe.GetValuePtrFromSlot(regInfo.GetValue());
468             auto objectHeader = bit_cast<ObjectHeader **, const ark::CFrame::SlotType *>(slotTypeRef);
469             auto stackRef = NewStackRef(objectHeader);
470             if (stackRef == stackRefInput) {
471                 res = true;
472                 return false;
473             }
474             return true;
475         });
476 
477         if (res) {
478             return true;
479         }
480     }
481     return false;
482 }
483 
484 }  // namespace ark::mem
485