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