1 /*
2 * Copyright (c) 2021 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
17 #include "runtime/mem/object_helpers-inl.h"
18 #include "runtime/include/coretypes/dyn_objects.h"
19 #include "runtime/include/hclass.h"
20 #include "runtime/include/class.h"
21 #include "runtime/include/coretypes/array-inl.h"
22 #include "runtime/include/coretypes/string.h"
23
24 #include "runtime/include/coretypes/class.h"
25 #include "runtime/include/thread.h"
26 #include "runtime/include/panda_vm.h"
27 #include "runtime/mem/gc/dynamic/gc_dynamic_data.h"
28
29 namespace panda::mem {
30
31 using DynClass = coretypes::DynClass;
32 using TaggedValue = coretypes::TaggedValue;
33 using TaggedType = coretypes::TaggedType;
34
GetObjectSize(const void * mem)35 size_t GetObjectSize(const void *mem)
36 {
37 ASSERT(mem != nullptr);
38 auto *obj_header = static_cast<const ObjectHeader *>(mem);
39 auto base_cls = obj_header->ClassAddr<BaseClass>();
40
41 size_t object_size;
42 if (base_cls->IsDynamicClass()) {
43 auto *klass = static_cast<HClass *>(base_cls);
44 if (klass->IsString()) {
45 auto *string_object = static_cast<const coretypes::String *>(obj_header);
46 object_size = string_object->ObjectSize();
47 } else if (klass->IsArray()) {
48 auto *array_object = static_cast<const coretypes::Array *>(obj_header);
49 object_size = sizeof(coretypes::Array) + array_object->GetLength() * TaggedValue::TaggedTypeSize();
50 } else {
51 object_size = base_cls->GetObjectSize();
52 }
53 } else {
54 object_size = obj_header->ObjectSize();
55 }
56 return object_size;
57 }
58
GetDebugInfoAboutObject(const ObjectHeader * header)59 PandaString GetDebugInfoAboutObject(const ObjectHeader *header)
60 {
61 PandaStringStream ss;
62 ss << "( " << header->ClassAddr<Class>()->GetDescriptor() << " " << std::hex << header << " " << std::dec
63 << GetObjectSize(header) << " bytes) mword = " << std::hex << header->AtomicGetMark().GetValue();
64 return ss.str();
65 }
66
DumpObject(ObjectHeader * object_header,std::basic_ostream<char,std::char_traits<char>> * o_stream)67 void DumpObject([[maybe_unused]] ObjectHeader *object_header,
68 std::basic_ostream<char, std::char_traits<char>> *o_stream)
69 {
70 auto *cls = object_header->ClassAddr<Class>();
71 ASSERT(cls != nullptr);
72 *o_stream << "Dump object object_header = " << std::hex << object_header << ", cls = " << std::hex << cls->GetName()
73 << std::endl;
74
75 if (cls->IsArrayClass()) {
76 auto array = static_cast<coretypes::Array *>(object_header);
77 *o_stream << "Array " << std::hex << object_header << " " << cls->GetComponentType()->GetName()
78 << " length = " << std::dec << array->GetLength() << std::endl;
79 return;
80 }
81
82 while (cls != nullptr) {
83 Span<Field> fields = cls->GetInstanceFields();
84 *o_stream << "Dump object: " << std::hex << object_header << std::endl;
85 if (cls->GetName() == "java.lang.String") {
86 auto *str_object = static_cast<panda::coretypes::String *>(object_header);
87 if (str_object->GetLength() > 0 && !str_object->IsUtf16()) {
88 *o_stream << "length = " << std::dec << str_object->GetLength() << std::endl;
89 constexpr size_t BUFF_SIZE = 256;
90 std::array<char, BUFF_SIZE> buff {0};
91 strncpy_s(&buff[0], BUFF_SIZE, reinterpret_cast<const char *>(str_object->GetDataMUtf8()),
92 static_cast<size_t>(str_object->GetLength()));
93 *o_stream << "String data: " << &buff[0] << std::endl;
94 }
95 }
96 for (Field &field : fields) {
97 *o_stream << "\tfield \"" << GetFieldName(field) << "\" ";
98 size_t offset = field.GetOffset();
99 panda_file::Type::TypeId type_id = field.GetType().GetId();
100 if (type_id == panda_file::Type::TypeId::REFERENCE) {
101 ObjectHeader *field_object = object_header->GetFieldObject(offset);
102 if (field_object != nullptr) {
103 *o_stream << std::hex << field_object << std::endl;
104 } else {
105 *o_stream << "NULL" << std::endl;
106 }
107 } else if (type_id != panda_file::Type::TypeId::VOID) {
108 *o_stream << std::dec;
109 switch (type_id) {
110 case panda_file::Type::TypeId::U1: {
111 auto val = object_header->GetFieldPrimitive<bool>(offset);
112 *o_stream << val << std::endl;
113 } break;
114 case panda_file::Type::TypeId::I8: {
115 auto val = object_header->GetFieldPrimitive<int8_t>(offset);
116 *o_stream << val << std::endl;
117 } break;
118 case panda_file::Type::TypeId::U8: {
119 auto val = object_header->GetFieldPrimitive<uint8_t>(offset);
120 *o_stream << val << std::endl;
121 } break;
122 case panda_file::Type::TypeId::I16: {
123 auto val = object_header->GetFieldPrimitive<int16_t>(offset);
124 *o_stream << val << std::endl;
125 } break;
126 case panda_file::Type::TypeId::U16: {
127 auto val = object_header->GetFieldPrimitive<uint16_t>(offset);
128 *o_stream << val << std::endl;
129 } break;
130 case panda_file::Type::TypeId::I32: {
131 auto val = object_header->GetFieldPrimitive<int32_t>(offset);
132 *o_stream << val << std::endl;
133 } break;
134 case panda_file::Type::TypeId::U32: {
135 auto val = object_header->GetFieldPrimitive<uint32_t>(offset);
136 *o_stream << val << std::endl;
137 } break;
138 case panda_file::Type::TypeId::F32: {
139 auto val = object_header->GetFieldPrimitive<float>(offset);
140 *o_stream << val << std::endl;
141 } break;
142 case panda_file::Type::TypeId::F64: {
143 auto val = object_header->GetFieldPrimitive<double>(offset);
144 *o_stream << val << std::endl;
145 } break;
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 case panda_file::Type::TypeId::U64: {
151 auto val = object_header->GetFieldPrimitive<uint64_t>(offset);
152 *o_stream << val << std::endl;
153 } break;
154 default:
155 LOG(FATAL, COMMON) << "Error at object dump - wrong type id";
156 }
157 }
158 }
159 cls = cls->GetBase();
160 }
161 }
162
DumpClass(Class * cls,std::basic_ostream<char,std::char_traits<char>> * o_stream)163 void DumpClass(Class *cls, std::basic_ostream<char, std::char_traits<char>> *o_stream)
164 {
165 if (UNLIKELY(cls == nullptr)) {
166 return;
167 }
168 std::function<void(Class *, ObjectHeader *, const Field *, ObjectHeader *)> field_dump(
169 [o_stream]([[maybe_unused]] Class *kls, [[maybe_unused]] ObjectHeader *obj, const Field *field,
170 ObjectHeader *field_object) {
171 *o_stream << "field = " << GetFieldName(*field) << std::hex << " " << field_object << std::endl;
172 });
173 // Dump class static fields
174 *o_stream << "Dump class: addr = " << std::hex << cls << ", cls = " << cls->GetDescriptor() << std::endl;
175 *o_stream << "Dump static fields:" << std::endl;
176 const Span<Field> &fields = cls->GetStaticFields();
177 ObjectHeader *cls_object = cls->GetManagedObject();
178 TraverseFields(fields, cls, cls_object, field_dump);
179 *o_stream << "Dump cls object fields:" << std::endl;
180 DumpObject(cls_object);
181 }
182
GetForwardAddress(ObjectHeader * object_header)183 ObjectHeader *GetForwardAddress(ObjectHeader *object_header)
184 {
185 ASSERT(object_header->IsForwarded());
186 MarkWord mark_word = object_header->AtomicGetMark();
187 MarkWord::markWordSize addr = mark_word.GetForwardingAddress();
188 return reinterpret_cast<ObjectHeader *>(addr);
189 }
190
GetFieldName(const Field & field)191 const char *GetFieldName(const Field &field)
192 {
193 static const char *empty_string = "";
194 bool is_proxy = field.GetClass()->IsProxy();
195 // For proxy class it is impossible to get field name in standard manner
196 if (!is_proxy) {
197 return reinterpret_cast<const char *>(field.GetName().data);
198 }
199 return empty_string;
200 }
201
TraverseAllObjects(ObjectHeader * object_header,const std::function<void (ObjectHeader *,ObjectHeader *)> & obj_visitor)202 void GCDynamicObjectHelpers::TraverseAllObjects(ObjectHeader *object_header,
203 const std::function<void(ObjectHeader *, ObjectHeader *)> &obj_visitor)
204 {
205 auto *cls = object_header->ClassAddr<HClass>();
206 ASSERT(cls != nullptr);
207 if (cls->IsString() || cls->IsNativePointer()) {
208 return;
209 }
210 if (cls->IsArray()) {
211 std::function<void(ObjectHeader *, const array_size_t, ObjectHeader *)> arr_fn(
212 [&obj_visitor]([[maybe_unused]] ObjectHeader *arr_object_header, [[maybe_unused]] const array_size_t INDEX,
213 ObjectHeader *object_reference) { obj_visitor(arr_object_header, object_reference); });
214 TraverseArray(object_header, cls, arr_fn);
215 } else {
216 std::function<void(ObjectHeader *, size_t, ObjectHeader *, bool)> dyn_obj_proxy(
217 [&obj_visitor](ObjectHeader *obj_header, [[maybe_unused]] size_t offset, ObjectHeader *obj_reference,
218 [[maybe_unused]] bool is_update_classword) { obj_visitor(obj_header, obj_reference); });
219 TraverseObject(object_header, cls, dyn_obj_proxy);
220 }
221 }
222
RecordDynWeakReference(GC * gc,coretypes::TaggedType * value)223 void GCDynamicObjectHelpers::RecordDynWeakReference(GC *gc, coretypes::TaggedType *value)
224 {
225 GCExtensionData *data = gc->GetExtensionData();
226 ASSERT(data != nullptr);
227 ASSERT(data->GetLangType() == LANG_TYPE_DYNAMIC);
228 static_cast<GCDynamicData *>(data)->GetDynWeakReferences()->push(value);
229 }
230
HandleDynWeakReferences(GC * gc)231 void GCDynamicObjectHelpers::HandleDynWeakReferences(GC *gc)
232 {
233 GCExtensionData *data = gc->GetExtensionData();
234 ASSERT(data != nullptr);
235 ASSERT(data->GetLangType() == LANG_TYPE_DYNAMIC);
236 auto *weak_refs = static_cast<GCDynamicData *>(data)->GetDynWeakReferences();
237 while (!weak_refs->empty()) {
238 coretypes::TaggedType *object_pointer = weak_refs->top();
239 weak_refs->pop();
240 TaggedValue value(*object_pointer);
241 if (value.IsUndefined()) {
242 continue;
243 }
244 ASSERT(value.IsWeak());
245 ObjectHeader *object = value.GetWeakReferent();
246 /* Note: If it is in young GC, the weak reference whose referent is in tenured space will not be marked. The */
247 /* weak reference whose referent is in young space will be moved into the tenured space or reset in */
248 /* CollecYoungAndMove. If the weak referent here is not moved in young GC, it should be cleared. */
249 if (gc->GetGCPhase() == GCPhase::GC_PHASE_MARK_YOUNG) {
250 if (gc->GetObjectAllocator()->IsAddressInYoungSpace(ToUintPtr(object)) && !gc->IsMarked(object)) {
251 *object_pointer = TaggedValue::Undefined().GetRawData();
252 }
253 } else {
254 /* Note: When it is in tenured GC, we check whether the referent has been marked. */
255 if (!gc->IsMarked(object)) {
256 *object_pointer = TaggedValue::Undefined().GetRawData();
257 }
258 }
259 }
260 }
261
TraverseAllObjects(ObjectHeader * object_header,const std::function<void (ObjectHeader *,ObjectHeader *)> & obj_visitor)262 void GCStaticObjectHelpers::TraverseAllObjects(ObjectHeader *object_header,
263 const std::function<void(ObjectHeader *, ObjectHeader *)> &obj_visitor)
264 {
265 auto *cls = object_header->ClassAddr<Class>();
266 // If create new object when visiting card table, the ClassAddr of the new object may be null
267 if (cls == nullptr) {
268 return;
269 }
270
271 if (cls->IsObjectArrayClass()) {
272 TraverseArray(object_header, cls, ArrayElementVisitor(obj_visitor));
273 } else {
274 if (cls->IsClassClass()) {
275 auto object_cls = panda::Class::FromClassObject(object_header);
276 if (object_cls->IsInitializing() || object_cls->IsInitialized()) {
277 TraverseClass(object_cls, ClassFieldVisitor(obj_visitor));
278 }
279 }
280 TraverseObject(object_header, cls, ObjectFieldVisitor(obj_visitor));
281 }
282 }
283
UpdateRefsToMovedObjects(PandaVM * vm,ObjectHeader * object,BaseClass * base_cls)284 void GCStaticObjectHelpers::UpdateRefsToMovedObjects(PandaVM *vm, ObjectHeader *object, BaseClass *base_cls)
285 {
286 ASSERT(!base_cls->IsDynamicClass());
287 auto *cls = static_cast<Class *>(base_cls);
288 if (cls->IsObjectArrayClass()) {
289 LOG_DEBUG_OBJ_HELPERS << " IsObjArrayClass";
290 TraverseArray(object, cls, [vm](ObjectHeader *obj, array_size_t index, ObjectHeader *element) {
291 MarkWord mark_word = element->GetMark(); // no need atomic because stw
292 if (mark_word.GetState() == MarkWord::ObjectState::STATE_GC) {
293 // update element without write barrier
294 auto array_object = static_cast<coretypes::Array *>(obj);
295 MarkWord::markWordSize addr = mark_word.GetForwardingAddress();
296 LOG_DEBUG_OBJ_HELPERS << " update obj ref for array " << std::hex << obj << " index = " << index
297 << " from " << array_object->Get<ObjectHeader *>(index) << " to " << addr;
298 array_object->Set<ObjectHeader *, false>(index, reinterpret_cast<ObjectHeader *>(addr));
299 }
300 });
301 } else {
302 LOG_DEBUG_OBJ_HELPERS << " IsObject";
303 TraverseObject(
304 object, cls, [vm](ObjectHeader *obj, ObjectHeader *field_object, uint32_t field_offset, bool is_volatile) {
305 MarkWord mark_word = field_object->GetMark(); // no need atomic because stw
306 if (mark_word.GetState() == MarkWord::ObjectState::STATE_GC) {
307 // update instance field without write barrier
308 MarkWord::markWordSize addr = mark_word.GetForwardingAddress();
309 LOG_DEBUG_OBJ_HELPERS << " update obj ref for object " << std::hex << obj << " from "
310 << field_object << " to " << addr;
311 if (is_volatile) {
312 obj->SetFieldObject<true, false>(field_offset, reinterpret_cast<ObjectHeader *>(addr));
313 } else {
314 obj->SetFieldObject<false, false>(field_offset, reinterpret_cast<ObjectHeader *>(addr));
315 }
316 }
317 });
318 if (!cls->IsClassClass()) {
319 return;
320 }
321
322 auto object_cls = panda::Class::FromClassObject(object);
323 if (!object_cls->IsInitializing() && !object_cls->IsInitialized()) {
324 return;
325 }
326
327 TraverseClass(
328 object_cls, [](Class *object_kls, ObjectHeader *field_object, uint32_t field_offset, bool is_volatile) {
329 MarkWord mark_word = field_object->GetMark(); // no need atomic because stw
330 if (mark_word.GetState() == MarkWord::ObjectState::STATE_GC) {
331 // update static field without write barrier
332 MarkWord::markWordSize addr = mark_word.GetForwardingAddress();
333 if (is_volatile) {
334 object_kls->SetFieldObject<true, false>(field_offset, reinterpret_cast<ObjectHeader *>(addr));
335 } else {
336 object_kls->SetFieldObject<false, false>(field_offset, reinterpret_cast<ObjectHeader *>(addr));
337 }
338 }
339 });
340 }
341 }
342
UpdateRefsToMovedObjects(PandaVM * vm,ObjectHeader * object,BaseClass * base_cls)343 void GCDynamicObjectHelpers::UpdateRefsToMovedObjects(PandaVM *vm, ObjectHeader *object, BaseClass *base_cls)
344 {
345 ASSERT(base_cls->IsDynamicClass());
346 auto *cls = static_cast<HClass *>(base_cls);
347 if (cls->IsNativePointer() || cls->IsString()) {
348 return;
349 }
350 if (cls->IsArray()) {
351 LOG_DEBUG_OBJ_HELPERS << " IsDynamicArrayClass";
352 auto update_array_callback = [vm](ObjectHeader *obj, array_size_t index, ObjectHeader *obj_ref) {
353 UpdateDynArray(vm, obj, index, obj_ref);
354 };
355 TraverseArray(object, cls, update_array_callback);
356 } else {
357 LOG_DEBUG_OBJ_HELPERS << " IsDynamicObject";
358 auto update_object_callback = [vm](ObjectHeader *obj, size_t offset, ObjectHeader *field_obj_ref,
359 bool is_update_classword) {
360 UpdateDynObjectRef(vm, obj, offset, field_obj_ref, is_update_classword);
361 };
362 TraverseObject(object, cls, update_object_callback);
363 }
364 }
365
UpdateDynArray(PandaVM * vm,ObjectHeader * object,array_size_t index,ObjectHeader * obj_ref)366 void GCDynamicObjectHelpers::UpdateDynArray(PandaVM *vm, ObjectHeader *object, array_size_t index,
367 ObjectHeader *obj_ref)
368 {
369 TaggedValue value(obj_ref);
370 bool is_dyn_weak = value.IsWeak();
371 if (is_dyn_weak) {
372 obj_ref = value.GetWeakReferent();
373 }
374
375 MarkWord mark_word = obj_ref->AtomicGetMark();
376 if (mark_word.GetState() == MarkWord::ObjectState::STATE_GC) {
377 auto arr = static_cast<coretypes::Array *>(object);
378 MarkWord::markWordSize addr = mark_word.GetForwardingAddress();
379 LOG_DEBUG_OBJ_HELPERS << " update obj ref for array " << std::hex << object << " index = " << index
380 << " from " << std::hex << arr->Get<ObjectHeader *>(index) << " to " << addr;
381 auto *field_object = reinterpret_cast<ObjectHeader *>(addr);
382 if (is_dyn_weak) {
383 field_object = TaggedValue(field_object).CreateAndGetWeakRef().GetRawHeapObject();
384 }
385 size_t offset = TaggedValue::TaggedTypeSize() * index;
386 ObjectAccessor::SetDynObject<true>(vm->GetAssociatedThread(), arr->GetData(), offset, field_object);
387 }
388 }
389
UpdateDynObjectRef(PandaVM * vm,ObjectHeader * object,size_t offset,ObjectHeader * field_obj_ref,bool is_update_classword)390 void GCDynamicObjectHelpers::UpdateDynObjectRef(PandaVM *vm, ObjectHeader *object, size_t offset,
391 ObjectHeader *field_obj_ref, bool is_update_classword)
392 {
393 TaggedValue value(field_obj_ref);
394 bool is_dyn_weak = value.IsWeak();
395 if (is_dyn_weak) {
396 field_obj_ref = value.GetWeakReferent();
397 }
398 MarkWord mark_word = field_obj_ref->AtomicGetMark();
399 if (mark_word.GetState() == MarkWord::ObjectState::STATE_GC) {
400 MarkWord::markWordSize addr = mark_word.GetForwardingAddress();
401 LOG_DEBUG_OBJ_HELPERS << " update obj ref for object " << std::hex << object << " from "
402 << ObjectAccessor::GetDynValue<ObjectHeader *>(object, offset) << " to " << addr;
403 auto *h_class = field_obj_ref->ClassAddr<HClass>();
404 if (is_update_classword && h_class->IsHClass()) {
405 addr += ObjectHeader::ObjectHeaderSize();
406 }
407 auto *field_object = reinterpret_cast<ObjectHeader *>(addr);
408 if (is_dyn_weak) {
409 field_object = TaggedValue(field_object).CreateAndGetWeakRef().GetRawHeapObject();
410 }
411 ObjectAccessor::SetDynObject(vm->GetAssociatedThread(), object, offset, field_object);
412 }
413 }
414
415 } // namespace panda::mem
416