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