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