• 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 #include <algorithm>
16 #include <cstdio>
17 #include <cstdint>
18 #include <cinttypes>
19 
20 #include "runtime/mem/object_helpers-inl.h"
21 
22 #include "libpandabase/utils/utf.h"
23 #include "runtime/include/thread.h"
24 #include "runtime/include/panda_vm.h"
25 #include "runtime/mem/free_object.h"
26 #include "runtime/mem/gc/dynamic/gc_dynamic_data.h"
27 
28 namespace ark::mem {
29 
30 using DynClass = coretypes::DynClass;
31 using TaggedValue = coretypes::TaggedValue;
32 using TaggedType = coretypes::TaggedType;
33 
GetDebugInfoAboutObject(const ObjectHeader * header)34 Logger::Buffer GetDebugInfoAboutObject(const ObjectHeader *header)
35 {
36     ValidateObject(nullptr, header);
37 
38     Logger::Buffer buffer;
39 // For Security reason we hide address
40 #if !defined(PANDA_TARGET_OHOS) || !defined(NDEBUG)
41     auto *baseClass = header->ClassAddr<BaseClass>();
42     const uint8_t *descriptor = nullptr;
43     if (baseClass->IsDynamicClass()) {
44         descriptor = utf::CStringAsMutf8("Dynamic");
45     } else {
46         descriptor = static_cast<Class *>(baseClass)->GetDescriptor();
47     }
48 
49     const void *rawptr = static_cast<const void *>(header);
50     uintmax_t mark = header->AtomicGetMark().GetValue();
51     size_t size = header->ObjectSize();
52 
53     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
54     buffer.Printf("(\"%s\" %p %zu bytes) mword = %" PRIuMAX, descriptor, rawptr, size, mark);
55 #endif
56 
57     return buffer;
58 }
59 
DumpArrayClassObject(ObjectHeader * objectHeader,std::basic_ostream<char,std::char_traits<char>> * oStream)60 static void DumpArrayClassObject(ObjectHeader *objectHeader, std::basic_ostream<char, std::char_traits<char>> *oStream)
61 {
62     auto *cls = objectHeader->ClassAddr<Class>();
63     ASSERT(cls->IsArrayClass());
64     auto array = static_cast<coretypes::Array *>(objectHeader);
65     *oStream << "Array " << std::hex << objectHeader << " " << cls->GetComponentType()->GetName()
66              << " length = " << std::dec << array->GetLength() << std::endl;
67 }
68 
DumpStringClass(ObjectHeader * objectHeader,std::basic_ostream<char,std::char_traits<char>> * oStream)69 static void DumpStringClass(ObjectHeader *objectHeader, std::basic_ostream<char, std::char_traits<char>> *oStream)
70 {
71     auto *strObject = static_cast<ark::coretypes::String *>(objectHeader);
72     if (strObject->GetLength() > 0 && !strObject->IsUtf16()) {
73         *oStream << "length = " << std::dec << strObject->GetLength() << std::endl;
74         constexpr size_t BUFF_SIZE = 256;
75         std::array<char, BUFF_SIZE> buff {0};
76         auto strRes = strncpy_s(&buff[0], BUFF_SIZE, reinterpret_cast<const char *>(strObject->GetDataMUtf8()),
77                                 std::min(BUFF_SIZE - 1, static_cast<size_t>(strObject->GetLength())));
78         if (UNLIKELY(strRes != EOK)) {
79             LOG(ERROR, RUNTIME) << "Couldn't copy string by strncpy_s, error code: " << strRes;
80         }
81         *oStream << "String data: " << &buff[0] << std::endl;
82     }
83 }
84 
DumpReferenceField(ObjectHeader * objectHeader,const Field & field,std::basic_ostream<char,std::char_traits<char>> * oStream)85 static void DumpReferenceField(ObjectHeader *objectHeader, const Field &field,
86                                std::basic_ostream<char, std::char_traits<char>> *oStream)
87 {
88     size_t offset = field.GetOffset();
89     ObjectHeader *fieldObject = objectHeader->GetFieldObject(offset);
90     if (fieldObject != nullptr) {
91         *oStream << std::hex << fieldObject << std::endl;
92     } else {
93         *oStream << "NULL" << std::endl;
94     }
95 }
96 
97 // CC-OFFNXT(G.FUN.01-CPP) big switch-case
DumpPrimitivesField(ObjectHeader * objectHeader,const Field & field,std::basic_ostream<char,std::char_traits<char>> * oStream)98 static void DumpPrimitivesField(ObjectHeader *objectHeader, const Field &field,
99                                 std::basic_ostream<char, std::char_traits<char>> *oStream)
100 {
101     size_t offset = field.GetOffset();
102     panda_file::Type::TypeId typeId = field.GetTypeId();
103     *oStream << std::dec;
104     switch (typeId) {
105         case panda_file::Type::TypeId::U1: {
106             auto val = objectHeader->GetFieldPrimitive<bool>(offset);
107             *oStream << val << std::endl;
108             break;
109         }
110         case panda_file::Type::TypeId::I8: {
111             auto val = objectHeader->GetFieldPrimitive<int8_t>(offset);
112             *oStream << val << std::endl;
113             break;
114         }
115         case panda_file::Type::TypeId::U8: {
116             auto val = objectHeader->GetFieldPrimitive<uint8_t>(offset);
117             *oStream << val << std::endl;
118             break;
119         }
120         case panda_file::Type::TypeId::I16: {
121             auto val = objectHeader->GetFieldPrimitive<int16_t>(offset);
122             *oStream << val << std::endl;
123             break;
124         }
125         case panda_file::Type::TypeId::U16: {
126             auto val = objectHeader->GetFieldPrimitive<uint16_t>(offset);
127             *oStream << val << std::endl;
128             break;
129         }
130         case panda_file::Type::TypeId::I32: {
131             auto val = objectHeader->GetFieldPrimitive<int32_t>(offset);
132             *oStream << val << std::endl;
133             break;
134         }
135         case panda_file::Type::TypeId::U32: {
136             auto val = objectHeader->GetFieldPrimitive<uint32_t>(offset);
137             *oStream << val << std::endl;
138             break;
139         }
140         case panda_file::Type::TypeId::F32: {
141             auto val = objectHeader->GetFieldPrimitive<float>(offset);
142             *oStream << val << std::endl;
143             break;
144         }
145         case panda_file::Type::TypeId::F64: {
146             auto val = objectHeader->GetFieldPrimitive<double>(offset);
147             *oStream << val << std::endl;
148             break;
149         }
150         case panda_file::Type::TypeId::I64: {
151             auto val = objectHeader->GetFieldPrimitive<int64_t>(offset);
152             *oStream << val << std::endl;
153             break;
154         }
155         case panda_file::Type::TypeId::U64: {
156             auto val = objectHeader->GetFieldPrimitive<uint64_t>(offset);
157             *oStream << val << std::endl;
158             break;
159         }
160         default:
161             LOG(FATAL, COMMON) << "Error at object dump - wrong type id";
162     }
163 }
164 
DumpObjectFields(ObjectHeader * objectHeader,std::basic_ostream<char,std::char_traits<char>> * oStream)165 void DumpObjectFields(ObjectHeader *objectHeader, std::basic_ostream<char, std::char_traits<char>> *oStream)
166 {
167     auto *cls = objectHeader->ClassAddr<Class>();
168     Span<Field> fields = cls->GetInstanceFields();
169     for (Field &field : fields) {
170         *oStream << "\tfield \"" << GetFieldName(field) << "\" ";
171         panda_file::Type::TypeId typeId = field.GetTypeId();
172         if (typeId == panda_file::Type::TypeId::REFERENCE) {
173             DumpReferenceField(objectHeader, field, oStream);
174         } else if (typeId != panda_file::Type::TypeId::VOID) {
175             DumpPrimitivesField(objectHeader, field, oStream);
176         }
177     }
178 }
179 
DumpObject(ObjectHeader * objectHeader,std::basic_ostream<char,std::char_traits<char>> * oStream)180 void DumpObject(ObjectHeader *objectHeader, std::basic_ostream<char, std::char_traits<char>> *oStream)
181 {
182     auto *cls = objectHeader->ClassAddr<Class>();
183     ASSERT(cls != nullptr);
184     *oStream << "Dump object object_header = " << std::hex << objectHeader << ", cls = " << std::hex << cls->GetName()
185              << std::endl;
186 
187     if (cls->IsArrayClass()) {
188         DumpArrayClassObject(objectHeader, oStream);
189     } else {
190         while (cls != nullptr) {
191             if (cls->IsStringClass()) {
192                 DumpStringClass(objectHeader, oStream);
193             }
194             DumpObjectFields(objectHeader, oStream);
195             cls = cls->GetBase();
196         }
197     }
198 }
199 
200 template <typename FieldVisitor>
TraverseFields(const Span<Field> & fields,const Class * cls,const ObjectHeader * objectHeader,const FieldVisitor & fieldVisitor)201 void TraverseFields(const Span<Field> &fields, const Class *cls, const ObjectHeader *objectHeader,
202                     const FieldVisitor &fieldVisitor)
203 {
204     for (const Field &field : fields) {
205         LOG(DEBUG, GC) << " current field \"" << GetFieldName(field) << "\"";
206         size_t offset = field.GetOffset();
207         panda_file::Type::TypeId typeId = field.GetTypeId();
208         if (typeId == panda_file::Type::TypeId::REFERENCE) {
209             ObjectHeader *fieldObject = objectHeader->GetFieldObject(offset);
210             if (fieldObject != nullptr) {
211                 LOG(DEBUG, GC) << " field val = " << std::hex << fieldObject;
212                 fieldVisitor(cls, objectHeader, &field, fieldObject);
213             } else {
214                 LOG(DEBUG, GC) << " field val = nullptr";
215             }
216         }
217     }
218 }
219 
DumpClass(const Class * cls,std::basic_ostream<char,std::char_traits<char>> * oStream)220 void DumpClass(const Class *cls, std::basic_ostream<char, std::char_traits<char>> *oStream)
221 {
222     if (UNLIKELY(cls == nullptr)) {
223         return;
224     }
225     std::function<void(const Class *, const ObjectHeader *, const Field *, ObjectHeader *)> fieldDump(
226         [oStream]([[maybe_unused]] const Class *kls, [[maybe_unused]] const ObjectHeader *obj, const Field *field,
227                   ObjectHeader *fieldObject) {
228             *oStream << "field = " << GetFieldName(*field) << std::hex << " " << fieldObject << std::endl;
229         });
230     // Dump class static fields
231     *oStream << "Dump class: addr = " << std::hex << cls << ", cls = " << cls->GetDescriptor() << std::endl;
232     *oStream << "Dump static fields:" << std::endl;
233     const Span<Field> &fields = cls->GetStaticFields();
234     ObjectHeader *clsObject = cls->GetManagedObject();
235     TraverseFields(fields, cls, clsObject, fieldDump);
236     *oStream << "Dump cls object fields:" << std::endl;
237     DumpObject(clsObject);
238 }
239 
GetForwardAddress(const ObjectHeader * objectHeader)240 ObjectHeader *GetForwardAddress(const ObjectHeader *objectHeader)
241 {
242     ASSERT(objectHeader->IsForwarded());
243     MarkWord markWord = objectHeader->AtomicGetMark();
244     MarkWord::MarkWordSize addr = markWord.GetForwardingAddress();
245     return reinterpret_cast<ObjectHeader *>(addr);
246 }
247 
GetFieldName(const Field & field)248 const char *GetFieldName(const Field &field)
249 {
250     static const char *emptyString = "";
251     const char *ret = emptyString;
252     bool isProxy = field.GetClass()->IsProxy();
253     // For proxy class it is impossible to get field name in standard manner
254     if (!isProxy) {
255         ret = reinterpret_cast<const char *>(field.GetName().data);
256     }
257     return ret;
258 }
259 
260 class StdFunctionAdapter {
261 public:
StdFunctionAdapter(const std::function<void (ObjectHeader *,ObjectHeader *)> & callback)262     explicit StdFunctionAdapter(const std::function<void(ObjectHeader *, ObjectHeader *)> &callback)
263         : callback_(callback)
264     {
265     }
266 
operator ()(ObjectHeader * obj,ObjectHeader * field,uint32_t offset,bool isVolatile)267     bool operator()(ObjectHeader *obj, ObjectHeader *field, [[maybe_unused]] uint32_t offset,
268                     [[maybe_unused]] bool isVolatile)
269     {
270         callback_(obj, field);
271         return true;
272     }
273 
274 private:
275     const std::function<void(ObjectHeader *, ObjectHeader *)> &callback_;
276 };
277 
TraverseAllObjects(ObjectHeader * objectHeader,const std::function<void (ObjectHeader *,ObjectHeader *)> & objVisitor)278 void GCDynamicObjectHelpers::TraverseAllObjects(ObjectHeader *objectHeader,
279                                                 const std::function<void(ObjectHeader *, ObjectHeader *)> &objVisitor)
280 {
281     StdFunctionAdapter handler(objVisitor);
282     TraverseAllObjectsWithInfo<false>(objectHeader, handler);
283 }
284 
RecordDynWeakReference(GC * gc,coretypes::TaggedType * value)285 void GCDynamicObjectHelpers::RecordDynWeakReference(GC *gc, coretypes::TaggedType *value)
286 {
287     GCExtensionData *data = gc->GetExtensionData();
288     ASSERT(data != nullptr);
289     ASSERT(data->GetLangType() == LANG_TYPE_DYNAMIC);
290     static_cast<GCDynamicData *>(data)->GetDynWeakReferences()->push(value);
291 }
292 
HandleDynWeakReferences(GC * gc)293 void GCDynamicObjectHelpers::HandleDynWeakReferences(GC *gc)
294 {
295     GCExtensionData *data = gc->GetExtensionData();
296     ASSERT(data != nullptr);
297     ASSERT(data->GetLangType() == LANG_TYPE_DYNAMIC);
298     auto *weakRefs = static_cast<GCDynamicData *>(data)->GetDynWeakReferences();
299     while (!weakRefs->empty()) {
300         coretypes::TaggedType *objectPointer = weakRefs->top();
301         weakRefs->pop();
302         TaggedValue value(*objectPointer);
303         if (value.IsUndefined()) {
304             continue;
305         }
306         ASSERT(value.IsWeak());
307         ObjectHeader *object = value.GetWeakReferent();
308         /* Note: If it is in young GC, the weak reference whose referent is in tenured space will not be marked. The */
309         /*       weak reference whose referent is in young space will be moved into the tenured space or reset in    */
310         /*       CollecYoungAndMove. If the weak referent here is not moved in young GC, it shoule be cleared.       */
311         if (gc->GetGCPhase() == GCPhase::GC_PHASE_MARK_YOUNG) {
312             if (gc->GetObjectAllocator()->IsObjectInYoungSpace(object) && !gc->IsMarked(object)) {
313                 *objectPointer = TaggedValue::Undefined().GetRawData();
314             }
315         } else {
316             /* Note: When it is in tenured GC, we check whether the referent has been marked. */
317             if (!gc->IsMarked(object)) {
318                 *objectPointer = TaggedValue::Undefined().GetRawData();
319             }
320         }
321     }
322 }
323 
TraverseAllObjects(ObjectHeader * objectHeader,const std::function<void (ObjectHeader *,ObjectHeader *)> & objVisitor)324 void GCStaticObjectHelpers::TraverseAllObjects(ObjectHeader *objectHeader,
325                                                const std::function<void(ObjectHeader *, ObjectHeader *)> &objVisitor)
326 {
327     StdFunctionAdapter handler(objVisitor);
328     TraverseAllObjectsWithInfo<false>(objectHeader, handler);
329 }
330 
331 class StaticUpdateHandler {
332 public:
operator ()(ObjectHeader * object,ObjectHeader * field,uint32_t offset,bool isVolatile)333     bool operator()(ObjectHeader *object, ObjectHeader *field, uint32_t offset, [[maybe_unused]] bool isVolatile)
334     {
335         GCStaticObjectHelpers::UpdateRefToMovedObject(object, field, offset);
336         return true;
337     }
338 };
339 
UpdateRefsToMovedObjects(ObjectHeader * object)340 void GCStaticObjectHelpers::UpdateRefsToMovedObjects(ObjectHeader *object)
341 {
342     StaticUpdateHandler handler;
343     TraverseAllObjectsWithInfo<false>(object, handler);
344 }
345 
UpdateRefToMovedObject(ObjectHeader * object,ObjectHeader * ref,uint32_t offset)346 ObjectHeader *GCStaticObjectHelpers::UpdateRefToMovedObject(ObjectHeader *object, ObjectHeader *ref, uint32_t offset)
347 {
348     MarkWord markWord = ref->GetMark();  // no need atomic because stw
349     if (markWord.GetState() != MarkWord::ObjectState::STATE_GC) {
350         return ref;
351     }
352     // update instance field without write barrier
353     MarkWord::MarkWordSize addr = markWord.GetForwardingAddress();
354     LOG(DEBUG, GC) << "update obj ref of object " << object << " from " << ref << " to " << ToVoidPtr(addr);
355     auto *forwardedObject = reinterpret_cast<ObjectHeader *>(addr);
356     object->SetFieldObject<false, false>(offset, forwardedObject);
357     return forwardedObject;
358 }
359 
360 class DynamicUpdateHandler {
361 public:
operator ()(ObjectHeader * object,ObjectHeader * field,uint32_t offset,bool isVolatile)362     bool operator()(ObjectHeader *object, ObjectHeader *field, uint32_t offset, [[maybe_unused]] bool isVolatile)
363     {
364         GCDynamicObjectHelpers::UpdateRefToMovedObject(object, field, offset);
365         return true;
366     }
367 };
368 
UpdateRefsToMovedObjects(ObjectHeader * object)369 void GCDynamicObjectHelpers::UpdateRefsToMovedObjects(ObjectHeader *object)
370 {
371     ASSERT(object->ClassAddr<HClass>()->IsDynamicClass());
372     DynamicUpdateHandler handler;
373     TraverseAllObjectsWithInfo<false>(object, handler);
374 }
375 
UpdateRefToMovedObject(ObjectHeader * object,ObjectHeader * ref,uint32_t offset)376 ObjectHeader *GCDynamicObjectHelpers::UpdateRefToMovedObject(ObjectHeader *object, ObjectHeader *ref, uint32_t offset)
377 {
378     MarkWord markWord = ref->AtomicGetMark();
379     if (markWord.GetState() != MarkWord::ObjectState::STATE_GC) {
380         return ref;
381     }
382     MarkWord::MarkWordSize addr = markWord.GetForwardingAddress();
383     LOG(DEBUG, GC) << "update obj ref for object " << object << " from "
384                    << ObjectAccessor::GetDynValue<ObjectHeader *>(object, offset) << " to " << ToVoidPtr(addr);
385     auto *forwardedObject = reinterpret_cast<ObjectHeader *>(addr);
386     if (ObjectAccessor::GetDynValue<TaggedValue>(object, offset).IsWeak()) {
387         forwardedObject = TaggedValue(forwardedObject).CreateAndGetWeakRef().GetRawHeapObject();
388     }
389     ObjectAccessor::SetDynValueWithoutBarrier(object, offset, TaggedValue(forwardedObject).GetRawData());
390     return forwardedObject;
391 }
392 
393 }  // namespace ark::mem
394