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