• 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 #ifndef PANDA_GLOBAL_OBJECT_STORAGE_H
16 #define PANDA_GLOBAL_OBJECT_STORAGE_H
17 
18 #include <libpandabase/os/mutex.h>
19 
20 #include "runtime/include/runtime.h"
21 #include "runtime/include/mem/panda_containers.h"
22 #include "runtime/include/object_header.h"
23 #include "runtime/mem/object_helpers.h"
24 #include "runtime/mem/gc/gc.h"
25 #include "runtime/mem/gc/gc_root.h"
26 #include "runtime/mem/gc/gc_phase.h"
27 #include "runtime/include/class.h"
28 #include "runtime/include/panda_vm.h"
29 #include "reference.h"
30 #include "utils/logger.h"
31 #include "utils/dfx.h"
32 
33 namespace ark::mem::test {
34 class ReferenceStorageTest;
35 }  // namespace ark::mem::test
36 
37 namespace ark::mem {
38 
39 /**
40  * Storage for objects which need to handle by GC. GC will handle moving these objects and will not reclaim then until
41  * user haven't called Remove method on this object.
42  * References will be removed automatically after Remove method or after storage's destructor.
43  */
44 class GlobalObjectStorage {
45 public:
46     explicit GlobalObjectStorage(mem::InternalAllocatorPtr allocator, size_t maxSize, bool enableSizeCheck);
47 
48     ~GlobalObjectStorage();
49 
50     /// Check whether ref is a valid global reference or not.
51     bool IsValidGlobalRef(const Reference *ref) const;
52 
53     /// Add object to the storage and return associated pointer with this object
54     PANDA_PUBLIC_API Reference *Add(const ObjectHeader *object, Reference::ObjectType type) const;
55 
56     /// Get stored object associated with given reference. Reference should be returned on Add method before.
Get(const Reference * reference)57     ObjectHeader *Get(const Reference *reference) const
58     {
59         if (reference == nullptr) {
60             return nullptr;
61         }
62         auto type = reference->GetType();
63         reference = Reference::GetRefWithoutType(reference);
64         AssertType(type);
65         ObjectHeader *result = nullptr;
66         if (type == Reference::ObjectType::GLOBAL) {
67             result = globalStorage_->Get(reference);
68         } else if (type == Reference::ObjectType::WEAK) {
69             result = weakStorage_->Get(reference);
70         } else {
71             result = globalFixedStorage_->Get(reference);
72         }
73         return result;
74     }
75 
GetAddressForRef(const Reference * reference)76     uintptr_t GetAddressForRef(const Reference *reference) const
77     {
78         ASSERT(reference != nullptr);
79         auto type = reference->GetType();
80         reference = Reference::GetRefWithoutType(reference);
81         AssertType(type);
82         uintptr_t result = 0;
83         if (type == Reference::ObjectType::GLOBAL) {
84             result = globalStorage_->GetAddressForRef(reference);
85         } else if (type == Reference::ObjectType::WEAK) {
86             result = weakStorage_->GetAddressForRef(reference);
87         } else {
88             result = globalFixedStorage_->GetAddressForRef(reference);
89         }
90         return result;
91     }
92 
93     /// Remove object from storage by given reference. Reference should be returned on Add method before.
94     PANDA_PUBLIC_API void Remove(const Reference *reference);
95 
96     /// Get all objects from storage. Used by debugging.
97     PandaVector<ObjectHeader *> GetAllObjects();
98 
99     void VisitObjects(const GCRootVisitor &gcRootVisitor, mem::RootType rootType);
100 
101     /// Update pointers to moved Objects in global storage.
102     // NOTE(alovkov): take a closure from gc
103     void UpdateMovedRefs(const GCRootUpdater &gcRootUpdater);
104 
105     void ClearUnmarkedWeakRefs(const GC *gc, const mem::GC::ReferenceClearPredicateT &pred);
106 
107     void ClearWeakRefs(const mem::GC::ReferenceClearPredicateT &pred);
108 
109     size_t GetSize();
110 
111     void Dump();
112 
113 private:
114     NO_COPY_SEMANTIC(GlobalObjectStorage);
115     NO_MOVE_SEMANTIC(GlobalObjectStorage);
116 
117     class ArrayStorage;
118 
119     static constexpr size_t GLOBAL_REF_SIZE_WARNING_LINE = 20;
120 
121     mem::InternalAllocatorPtr allocator_;
122     ArrayStorage *globalStorage_;
123     ArrayStorage *globalFixedStorage_;
124     ArrayStorage *weakStorage_;
125 
AssertType(Reference::ObjectType type)126     static void AssertType([[maybe_unused]] Reference::ObjectType type)
127     {
128         ASSERT(type == Reference::ObjectType::GLOBAL || type == Reference::ObjectType::GLOBAL_FIXED ||
129                type == Reference::ObjectType::WEAK);
130     }
131 
132     friend class ::ark::mem::test::ReferenceStorageTest;
133 
134     class ArrayStorage {
135 #ifndef NDEBUG
136         // for better coverage of EnsureCapacity
137         static constexpr size_t INITIAL_SIZE = 2;
138 #else
139         static constexpr size_t INITIAL_SIZE = 128;
140 #endif  // NDEBUG
141         static constexpr size_t FREE_INDEX_BIT = 0;
142         static constexpr size_t BITS_FOR_TYPE = 2U;
143         static constexpr size_t BITS_FOR_INDEX = 1U;
144         static constexpr size_t ENSURE_CAPACITY_MULTIPLIER = 2;
145 
146         /**
147          * There are 2 cases:
148          * 1) When index is busy - then we store jobject in storage_ and 0 in the lowest bit (cause of alignment).
149          * Reference* contains it's index shifted by 2 with reference-type in lowest bits which we return to user and
150          * doesn't stores inside storage explicity.
151          *
152          * 2) When index if free - storage[index] stores next free index (shifted by 1) with lowest bit equals to 1
153          */
154         /*
155         |-----------------------------------------------------|------------------|------------------|
156         |      Case          |         Highest bits           | [1] lowest bit   | [0] lowest bit   |
157         --------------------------------------------------------------------------------------------|
158         | busy-index         |                                |                  |                  |
159         | Reference* (index) |            index               |   0/1 (ref-type) |   0/1 (ref-type) |
160         | storage[index]     |                           xxx                     |        0         |
161         ---------------------|--------------------------------|------------------|-------------------
162         | free-index         |                                                   |                  |
163         | storage[index]     |                        xxx                        |        1         |
164         ---------------------------------------------------------------------------------------------
165         */
166 
GUARDED_BY(mutex_)167         PandaVector<uintptr_t> storage_ GUARDED_BY(mutex_) {};
168         /// Index of first available block in list
169         uintptr_t firstAvailableBlock_;
170         /// How many blocks are available in current storage (can be increased if size less than max size)
171         size_t blocksAvailable_;
172 
173         bool enableSizeCheck_;
174         bool isFixed_;
175         size_t maxSize_;
176 
177         mutable os::memory::RWLock mutex_;
178         mem::InternalAllocatorPtr allocator_;
179 
180     public:
181         explicit ArrayStorage(mem::InternalAllocatorPtr allocator, size_t maxSize, bool enableSizeCheck,
182                               bool isFixed = false)
enableSizeCheck_(enableSizeCheck)183             : enableSizeCheck_(enableSizeCheck), isFixed_(isFixed), maxSize_(maxSize), allocator_(allocator)
184         {
185             ASSERT(maxSize < (std::numeric_limits<uintptr_t>::max() >> (BITS_FOR_TYPE)));
186 
187             blocksAvailable_ = isFixed ? maxSize : INITIAL_SIZE;
188             firstAvailableBlock_ = 0;
189 
190             storage_.resize(blocksAvailable_);
191             for (size_t i = 0; i < storage_.size() - 1; i++) {
192                 storage_[i] = EncodeNextIndex(i + 1);
193             }
194             storage_[storage_.size() - 1] = 0;
195         }
196 
197         ~ArrayStorage() = default;
198 
199         NO_COPY_SEMANTIC(ArrayStorage);
200         NO_MOVE_SEMANTIC(ArrayStorage);
201 
Add(const ObjectHeader * object)202         Reference *Add(const ObjectHeader *object)
203         {
204             ASSERT(object != nullptr);
205             os::memory::WriteLockHolder lk(mutex_);
206 
207             if (blocksAvailable_ == 0) {
208                 if (storage_.size() * ENSURE_CAPACITY_MULTIPLIER <= maxSize_ && !isFixed_) {
209                     EnsureCapacity();
210                 } else {
211                     LOG(ERROR, GC) << "Global reference storage is full";
212                     Dump();
213                     return nullptr;
214                 }
215             }
216             ASSERT(blocksAvailable_ != 0);
217             auto nextBlock = DecodeIndex(storage_[firstAvailableBlock_]);
218             auto currentIndex = firstAvailableBlock_;
219             AssertIndex(currentIndex);
220 
221             auto addr = reinterpret_cast<uintptr_t>(object);
222             [[maybe_unused]] uintptr_t lastBit = BitField<uintptr_t, FREE_INDEX_BIT>::Get(addr);
223             ASSERT(lastBit == 0);  // every object should be alignmented
224 
225             storage_[currentIndex] = addr;
226             auto ref = IndexToReference(currentIndex);
227             firstAvailableBlock_ = nextBlock;
228             blocksAvailable_--;
229 
230             CheckAlmostOverflow();
231             return ref;
232         }
233 
EnsureCapacity()234         void EnsureCapacity() REQUIRES(mutex_)
235         {
236             auto prevLength = storage_.size();
237             size_t newLength = storage_.size() * ENSURE_CAPACITY_MULTIPLIER;
238             blocksAvailable_ = firstAvailableBlock_ = prevLength;
239             storage_.resize(newLength);
240             for (size_t i = prevLength; i < newLength - 1; i++) {
241                 storage_[i] = EncodeNextIndex(i + 1);
242             }
243             storage_[storage_.size() - 1] = 0;
244             LOG(DEBUG, GC) << "Increase global storage from: " << prevLength << " to: " << newLength;
245         }
246 
CheckAlmostOverflow()247         void CheckAlmostOverflow() REQUIRES_SHARED(mutex_)
248         {
249             size_t nowSize = GetSize();
250             if (enableSizeCheck_ && nowSize >= maxSize_ - GLOBAL_REF_SIZE_WARNING_LINE) {
251                 LOG(INFO, GC) << "Global reference storage almost overflow. now size: " << nowSize
252                               << ", max size: " << maxSize_;
253                 // NOTE(xucheng): Dump global reference storage info now. May use Thread::Dump() when it can be used.
254                 Dump();
255             }
256         }
257 
Get(const Reference * ref)258         ObjectHeader *Get(const Reference *ref) const
259         {
260             os::memory::ReadLockHolder lk(mutex_);
261             auto index = ReferenceToIndex(ref);
262             return reinterpret_cast<ObjectHeader *>(storage_[index]);
263         }
264 
GetAddressForRef(const Reference * ref)265         uintptr_t GetAddressForRef(const Reference *ref) const
266         {
267             os::memory::ReadLockHolder lk(mutex_);
268             ASSERT(isFixed_);
269             auto index = ReferenceToIndex(ref);
270             return reinterpret_cast<uintptr_t>(&storage_[index]);
271         }
272 
Remove(const Reference * ref)273         void Remove(const Reference *ref)
274         {
275             os::memory::WriteLockHolder lk(mutex_);
276             auto index = ReferenceToIndex(ref);
277             storage_[index] = EncodeNextIndex(firstAvailableBlock_);
278             firstAvailableBlock_ = index;
279             blocksAvailable_++;
280         }
281 
UpdateMovedRefs(const GCRootUpdater & gcRootUpdater)282         void UpdateMovedRefs(const GCRootUpdater &gcRootUpdater)
283         {
284             os::memory::WriteLockHolder lk(mutex_);
285             // NOLINTNEXTLINE(modernize-loop-convert)
286             for (size_t index = 0; index < storage_.size(); index++) {
287                 auto ref = storage_[index];
288                 if (IsBusy(ref)) {
289                     auto obj = reinterpret_cast<ObjectHeader *>(ref);
290                     if (obj != nullptr && gcRootUpdater(&obj)) {
291                         storage_[index] = ToUintPtr(obj);
292                     }
293                 }
294             }
295         }
296 
VisitObjects(const GCRootVisitor & gcRootVisitor,mem::RootType rootType)297         void VisitObjects(const GCRootVisitor &gcRootVisitor, mem::RootType rootType)
298         {
299             os::memory::ReadLockHolder lk(mutex_);
300 
301             for (const auto &ref : storage_) {
302                 if (IsBusy(ref)) {
303                     auto obj = reinterpret_cast<ObjectHeader *>(ref);
304                     if (obj != nullptr) {
305                         LOG(DEBUG, GC) << " Found root from global storage: " << mem::GetDebugInfoAboutObject(obj);
306                         gcRootVisitor({rootType, obj});
307                     }
308                 }
309             }
310         }
311 
ClearWeakRefs(const mem::GC::ReferenceClearPredicateT & pred)312         void ClearWeakRefs(const mem::GC::ReferenceClearPredicateT &pred)
313         {
314             os::memory::WriteLockHolder lk(mutex_);
315 
316             for (auto &ref : storage_) {
317                 if (IsBusy(ref)) {
318                     auto obj = reinterpret_cast<ObjectHeader *>(ref);
319                     if (obj != nullptr && pred(obj)) {
320                         LOG(DEBUG, GC) << "Clear weak-reference: " << obj;
321                         ref = reinterpret_cast<uintptr_t>(nullptr);
322                     }
323                 }
324             }
325         }
326 
GetAllObjects()327         PandaVector<ObjectHeader *> GetAllObjects()
328         {
329             auto objects = PandaVector<ObjectHeader *>(allocator_->Adapter());
330             {
331                 os::memory::ReadLockHolder lk(mutex_);
332                 for (const auto &ref : storage_) {
333                     // we don't return nulls on GetAllObjects
334                     if (ref != 0 && IsBusy(ref)) {
335                         auto obj = reinterpret_cast<ObjectHeader *>(ref);
336                         objects.push_back(obj);
337                     }
338                 }
339             }
340             return objects;
341         }
342 
IsValidGlobalRef(const Reference * ref)343         bool IsValidGlobalRef(const Reference *ref)
344         {
345             ASSERT(ref != nullptr);
346             os::memory::ReadLockHolder lk(mutex_);
347             uintptr_t index = ReferenceToIndex<false>(ref);
348             if (index >= storage_.size()) {
349                 return false;
350             }
351             if (IsFreeIndex(index)) {
352                 return false;
353             }
354             return index < storage_.size();
355         }
356 
DumpWithLock()357         void DumpWithLock()
358         {
359             os::memory::ReadLockHolder lk(mutex_);
360             Dump();
361         }
362 
Dump()363         void Dump() REQUIRES_SHARED(mutex_)
364         {
365             if (DfxController::IsInitialized() &&
366                 DfxController::GetOptionValue(DfxOptionHandler::REFERENCE_DUMP) != 1) {
367                 return;
368             }
369             static constexpr size_t DUMP_NUMS = 20;
370             size_t num = 0;
371             LOG(INFO, GC) << "Dump the last " << DUMP_NUMS << " global references info:";
372 
373             for (auto it = storage_.rbegin(); it != storage_.rend(); it++) {
374                 uintptr_t ref = *it;
375                 if (IsBusy(ref)) {
376                     auto obj = reinterpret_cast<ObjectHeader *>(ref);
377                     LOG(INFO, GC) << "\t Index: " << GetSize() - num << ", Global reference: " << std::hex << ref
378                                   << ", Object: " << std::hex << obj
379                                   << ", Class: " << obj->ClassAddr<ark::Class>()->GetName();
380                     num++;
381                     if (num == DUMP_NUMS || num > GetSize()) {
382                         break;
383                     }
384                 }
385             }
386         }
387 
GetSize()388         size_t GetSize() const REQUIRES_SHARED(mutex_)
389         {
390             return storage_.size() - blocksAvailable_;
391         }
392 
GetSizeWithLock()393         size_t GetSizeWithLock() const
394         {
395             os::memory::ReadLockHolder globalLock(mutex_);
396             return GetSize();
397         }
398 
IsFreeIndex(uintptr_t index)399         bool IsFreeIndex(uintptr_t index) REQUIRES_SHARED(mutex_)
400         {
401             return IsFreeValue(storage_[index]);
402         }
403 
IsFreeValue(uintptr_t value)404         bool IsFreeValue(uintptr_t value)
405         {
406             uintptr_t lastBit = BitField<uintptr_t, FREE_INDEX_BIT>::Get(value);
407             return lastBit == 1;
408         }
409 
IsBusy(uintptr_t value)410         bool IsBusy(uintptr_t value)
411         {
412             return !IsFreeValue(value);
413         }
414 
EncodeObjectIndex(uintptr_t index)415         static uintptr_t EncodeObjectIndex(uintptr_t index)
416         {
417             ASSERT(index < (std::numeric_limits<uintptr_t>::max() >> BITS_FOR_INDEX));
418             return index << BITS_FOR_INDEX;
419         }
420 
EncodeNextIndex(uintptr_t index)421         static uintptr_t EncodeNextIndex(uintptr_t index)
422         {
423             uintptr_t shiftedIndex = EncodeObjectIndex(index);
424             BitField<uintptr_t, FREE_INDEX_BIT>::Set(1, &shiftedIndex);
425             return shiftedIndex;
426         }
427 
DecodeIndex(uintptr_t index)428         static uintptr_t DecodeIndex(uintptr_t index)
429         {
430             return index >> BITS_FOR_INDEX;
431         }
432 
433         /**
434          * We need to add 1 to not return nullptr to distinct it from situation when we couldn't create a reference.
435          * Shift by 2 is needed because every Reference stores type in lowest 2 bits.
436          */
IndexToReference(uintptr_t encodedIndex)437         Reference *IndexToReference(uintptr_t encodedIndex) const REQUIRES_SHARED(mutex_)
438         {
439             AssertIndex(DecodeIndex(encodedIndex));
440             return reinterpret_cast<Reference *>((encodedIndex + 1) << BITS_FOR_TYPE);
441         }
442 
443         template <bool CHECK_ASSERT = true>
ReferenceToIndex(const Reference * ref)444         uintptr_t ReferenceToIndex(const Reference *ref) const REQUIRES_SHARED(mutex_)
445         {
446             if (CHECK_ASSERT) {
447                 AssertIndex(ref);
448             }
449             return (reinterpret_cast<uintptr_t>(ref) >> BITS_FOR_TYPE) - 1;
450         }
451 
AssertIndex(const Reference * ref)452         void AssertIndex(const Reference *ref) const REQUIRES_SHARED(mutex_)
453         {
454             auto decodedIndex = (reinterpret_cast<uintptr_t>(ref) >> BITS_FOR_TYPE) - 1;
455             AssertIndex(DecodeIndex(decodedIndex));
456         }
457 
AssertIndex(uintptr_t index)458         void AssertIndex([[maybe_unused]] uintptr_t index) const REQUIRES_SHARED(mutex_)
459         {
460             ASSERT(static_cast<uintptr_t>(index) < storage_.size());
461         }
462 
463         // test usage only
GetVectorSize()464         size_t GetVectorSize()
465         {
466             os::memory::ReadLockHolder lk(mutex_);
467             return storage_.size();
468         }
469 
470         friend class ::ark::mem::test::ReferenceStorageTest;
471     };
472 };
473 }  // namespace ark::mem
474 #endif  // PANDA_GLOBAL_OBJECT_STORAGE_H
475