Flutter Engine
The Flutter Engine
object_graph_test.cc
Go to the documentation of this file.
1// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/object_graph.h"
6#include "platform/assert.h"
7#include "vm/unit_test.h"
8
9namespace dart {
10
11#if !defined(PRODUCT)
12
14 public:
15 // Records the number of objects and total size visited, excluding 'skip'
16 // and any objects only reachable through 'skip'.
18 : count_(0), size_(0), skip_(skip), expected_parent_(expected_parent) {}
19
20 virtual Direction VisitObject(ObjectGraph::StackIterator* it) {
21 ObjectPtr obj = it->Get();
22 if (obj == skip_) {
23 EXPECT(it->MoveToParent());
24 EXPECT_EQ(expected_parent_, it->Get());
25 return kBacktrack;
26 }
27 ++count_;
28 size_ += obj->untag()->HeapSize();
29 return kProceed;
30 }
31
32 int count() const { return count_; }
33 int size() const { return size_; }
34
35 private:
36 int count_;
37 intptr_t size_;
38 ObjectPtr skip_;
39 ObjectPtr expected_parent_;
40};
41
43 auto heap = thread->isolate_group()->heap();
44
45 // Create a simple object graph with objects a, b, c, d:
46 // a+->b+->c
47 // +   +
48 // |   v
49 // +-->d
54 a.SetAt(10, b);
55 b.SetAt(0, c);
56 b.SetAt(1, d);
57 a.SetAt(11, d);
58 intptr_t a_size = a.ptr()->untag()->HeapSize();
59 intptr_t b_size = b.ptr()->untag()->HeapSize();
60 intptr_t c_size = c.ptr()->untag()->HeapSize();
61 intptr_t d_size = d.ptr()->untag()->HeapSize();
62 {
63 // No more allocation; raw pointers ahead.
64 GcSafepointOperationScope safepoint(thread);
65 ObjectPtr b_raw = b.ptr();
66 // Clear handles to cut unintended retained paths.
67 b = Array::null();
68 c = Array::null();
69 d = Array::null();
70 ObjectGraph graph(thread);
71 {
72 HeapIterationScope iteration_scope(thread, true);
73 {
74 // Compare count and size when 'b' is/isn't skipped.
76 graph.IterateObjectsFrom(a, &with);
77 CounterVisitor without(b_raw, a.ptr());
78 graph.IterateObjectsFrom(a, &without);
79 // Only 'b' and 'c' were cut off.
80 EXPECT_EQ(2, with.count() - without.count());
81 EXPECT_EQ(b_size + c_size, with.size() - without.size());
82 }
83 {
84 // Like above, but iterate over the entire isolate. The counts and sizes
85 // are thus larger, but the difference should still be just 'b' and 'c'.
87 graph.IterateObjects(&with);
88 CounterVisitor without(b_raw, a.ptr());
89 graph.IterateObjects(&without);
90 EXPECT_EQ(2, with.count() - without.count());
91 EXPECT_EQ(b_size + c_size, with.size() - without.size());
92 }
93 }
94 EXPECT_EQ(a_size + b_size + c_size + d_size,
95 graph.SizeRetainedByInstance(a));
96 }
97 {
98 // Get hold of c again.
99 b ^= a.At(10);
100 c ^= b.At(0);
101 b = Array::null();
102 ObjectGraph graph(thread);
103 // A retaining path should end like this: c <- b <- a <- ...
104 {
105 HANDLESCOPE(thread);
106 // Test null, empty, and length 1 array.
107 intptr_t null_length =
108 graph.RetainingPath(&c, Object::null_array()).length;
109 intptr_t empty_length =
110 graph.RetainingPath(&c, Object::empty_array()).length;
112 intptr_t one_length = graph.RetainingPath(&c, path).length;
113 EXPECT_EQ(null_length, empty_length);
114 EXPECT_EQ(null_length, one_length);
115 EXPECT_LE(3, null_length);
116 }
117 {
118 HANDLESCOPE(thread);
120 // Trigger a full GC to increase probability of concurrent tasks.
121 heap->CollectAllGarbage();
122 intptr_t length = graph.RetainingPath(&c, path).length;
123 EXPECT_LE(3, length);
124 Array& expected_c = Array::Handle();
125 expected_c ^= path.At(0);
126 // c is the first element in b.
127 Smi& offset_from_parent = Smi::Handle();
128 offset_from_parent ^= path.At(1);
129 EXPECT_EQ(Array::element_offset(0), offset_from_parent.Value());
130 Array& expected_b = Array::Handle();
131 expected_b ^= path.At(2);
132 // b is the element with index 10 in a.
133 offset_from_parent ^= path.At(3);
134 EXPECT_EQ(Array::element_offset(10), offset_from_parent.Value());
135 Array& expected_a = Array::Handle();
136 expected_a ^= path.At(4);
137 EXPECT(expected_c.ptr() == c.ptr());
138 EXPECT(expected_b.ptr() == a.At(10));
139 EXPECT(expected_a.ptr() == a.ptr());
140 }
141 }
142}
143
144static void WeakHandleFinalizer(void* isolate_callback_data, void* peer) {}
145
146ISOLATE_UNIT_TEST_CASE(RetainingPathGCRoot) {
147 Dart_PersistentHandle persistent_handle;
148 Dart_WeakPersistentHandle weak_persistent_handle;
150 ObjectGraph graph(thread);
151 Dart_Handle handle = Api::NewHandle(thread, path.ptr());
152
153 // GC root should be a local handle
154 auto result = graph.RetainingPath(&path, path);
155 EXPECT_STREQ(result.gc_root_type, "local handle");
156
157 // GC root should now be a weak persistent handle
158 {
159 TransitionVMToNative transition(thread);
160 weak_persistent_handle = Dart_NewWeakPersistentHandle(
161 handle, reinterpret_cast<void*>(0xdeadbeef), 128, WeakHandleFinalizer);
162 }
163 result = graph.RetainingPath(&path, path);
164 EXPECT_STREQ(result.gc_root_type, "weak persistent handle");
165
166 // GC root should now be a persistent handle
167 {
168 TransitionVMToNative transition(thread);
169 persistent_handle = Dart_NewPersistentHandle(handle);
170 }
171 result = graph.RetainingPath(&path, path);
172 EXPECT_STREQ(result.gc_root_type, "persistent handle");
173
174 // Delete the persistent handle. GC root should now be weak persistent handle
175 {
176 TransitionVMToNative transition(thread);
177 Dart_DeletePersistentHandle(persistent_handle);
178 persistent_handle = nullptr;
179 }
180 result = graph.RetainingPath(&path, path);
181 EXPECT_STREQ(result.gc_root_type, "weak persistent handle");
182
183 // Delete the weak persistent handle. GC root should now be local handle.
184 {
185 TransitionVMToNative transition(thread);
186 Dart_DeleteWeakPersistentHandle(weak_persistent_handle);
187 weak_persistent_handle = nullptr;
188 }
189 result = graph.RetainingPath(&path, path);
190 EXPECT_STREQ(result.gc_root_type, "local handle");
191}
192
193#endif // !defined(PRODUCT)
194
195} // namespace dart
static bool skip(SkStream *stream, size_t amount)
#define EXPECT(type, expectedAlignment, expectedSize)
static Dart_Handle NewHandle(Thread *thread, ObjectPtr raw)
static intptr_t element_offset(intptr_t index)
Definition: object.h:10838
static ArrayPtr New(intptr_t len, Heap::Space space=Heap::kNew)
Definition: object.h:10959
CounterVisitor(ObjectPtr skip, ObjectPtr expected_parent)
virtual Direction VisitObject(ObjectGraph::StackIterator *it)
@ kNew
Definition: heap.h:38
@ kOld
Definition: heap.h:39
UntaggedObject * untag() const
static ObjectPtr null()
Definition: object.h:433
ObjectPtr ptr() const
Definition: object.h:332
static Object & Handle()
Definition: object.h:407
intptr_t Value() const
Definition: object.h:9990
intptr_t HeapSize() const
Definition: raw_object.h:401
struct _Dart_Handle * Dart_Handle
Definition: dart_api.h:258
Dart_Handle Dart_PersistentHandle
Definition: dart_api.h:259
struct _Dart_WeakPersistentHandle * Dart_WeakPersistentHandle
Definition: dart_api.h:260
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
static bool b
struct MyStruct a[10]
GAsyncResult * result
#define HANDLESCOPE(thread)
Definition: handles.h:321
size_t length
Visitor(Ts...) -> Visitor< Ts... >
Definition: dart_vm.cc:33
DART_EXPORT Dart_WeakPersistentHandle Dart_NewWeakPersistentHandle(Dart_Handle object, void *peer, intptr_t external_allocation_size, Dart_HandleFinalizer callback)
DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object)
DART_EXPORT void Dart_DeleteWeakPersistentHandle(Dart_WeakPersistentHandle object)
ISOLATE_UNIT_TEST_CASE(StackAllocatedDestruction)
static void WeakHandleFinalizer(void *isolate_callback_data, void *peer)
DART_EXPORT Dart_PersistentHandle Dart_NewPersistentHandle(Dart_Handle object)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57