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