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