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 ¤t_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 ®_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