OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/object_graph.h" | 5 #include "vm/object_graph.h" |
6 | 6 |
7 #include "vm/dart.h" | 7 #include "vm/dart.h" |
8 #include "vm/growable_array.h" | 8 #include "vm/growable_array.h" |
9 #include "vm/isolate.h" | 9 #include "vm/isolate.h" |
10 #include "vm/object.h" | 10 #include "vm/object.h" |
11 #include "vm/raw_object.h" | 11 #include "vm/raw_object.h" |
12 #include "vm/visitor.h" | 12 #include "vm/visitor.h" |
13 | 13 |
14 namespace dart { | 14 namespace dart { |
15 | 15 |
16 // The state of a pre-order, depth-first traversal of an object graph. | 16 // The state of a pre-order, depth-first traversal of an object graph. |
17 // When a node is visited, *all* its children are pushed to the stack at once. | 17 // When a node is visited, *all* its children are pushed to the stack at once. |
18 // We insert a sentinel between the node and its children on the stack, to | 18 // We insert a sentinel between the node and its children on the stack, to |
19 // remember that the node has been visited. The node is kept on the stack while | 19 // remember that the node has been visited. The node is kept on the stack while |
20 // its children are processed, to give the visitor a complete chain of parents. | 20 // its children are processed, to give the visitor a complete chain of parents. |
21 // | 21 // |
22 // TODO(koda): Potential optimizations: | 22 // TODO(koda): Potential optimizations: |
23 // - Linked chunks instead of std::vector. | 23 // - Use tag bit instead of full-word sentinel. |
24 // - Use smi/heap tag bit instead of full-word sentinel. | |
25 // - Extend RawObject with incremental iteration (one child at a time). | |
26 class ObjectGraph::Stack : public ObjectPointerVisitor { | 24 class ObjectGraph::Stack : public ObjectPointerVisitor { |
27 public: | 25 public: |
28 explicit Stack(Isolate* isolate) | 26 explicit Stack(Isolate* isolate) |
29 : ObjectPointerVisitor(isolate), data_(kInitialCapacity) { } | 27 : ObjectPointerVisitor(isolate), data_(kInitialCapacity) { } |
30 | 28 |
31 // Marks and pushes. Used to initialize this stack with roots. | 29 // Marks and pushes. Used to initialize this stack with roots. |
32 virtual void VisitPointers(RawObject** first, RawObject** last) { | 30 virtual void VisitPointers(RawObject** first, RawObject** last) { |
33 for (RawObject** current = first; current <= last; ++current) { | 31 for (RawObject** current = first; current <= last; ++current) { |
34 if ((*current)->IsHeapObject() && !(*current)->IsMarked()) { | 32 if ((*current)->IsHeapObject() && !(*current)->IsMarked()) { |
35 (*current)->SetMarkBit(); | 33 (*current)->SetMarkBit(); |
36 data_.Add(*current); | 34 data_.Add(current); |
37 } | 35 } |
38 } | 36 } |
39 } | 37 } |
40 | 38 |
41 // Traverses the object graph from the current state. | 39 // Traverses the object graph from the current state. |
42 void TraverseGraph(ObjectGraph::Visitor* visitor) { | 40 void TraverseGraph(ObjectGraph::Visitor* visitor) { |
43 while (!data_.is_empty()) { | 41 while (!data_.is_empty()) { |
44 RawObject* obj = data_.Last(); | 42 RawObject** obj_ptr = data_.Last(); |
45 if (obj == kSentinel) { | 43 if (obj_ptr == kSentinel) { |
46 data_.RemoveLast(); | 44 data_.RemoveLast(); |
47 // The node below the sentinel has already been visited. | 45 // The node below the sentinel has already been visited. |
48 data_.RemoveLast(); | 46 data_.RemoveLast(); |
49 continue; | 47 continue; |
50 } | 48 } |
51 ASSERT(obj->IsHeapObject()); | 49 ASSERT((*obj_ptr)->IsHeapObject()); |
52 data_.Add(kSentinel); | 50 data_.Add(kSentinel); |
53 StackIterator it(this, data_.length() - 2); | 51 StackIterator it(this, data_.length() - 2); |
54 switch (visitor->VisitObject(&it)) { | 52 switch (visitor->VisitObject(&it)) { |
55 case ObjectGraph::Visitor::kProceed: | 53 case ObjectGraph::Visitor::kProceed: |
56 obj->VisitPointers(this); | 54 (*obj_ptr)->VisitPointers(this); |
57 break; | 55 break; |
58 case ObjectGraph::Visitor::kBacktrack: | 56 case ObjectGraph::Visitor::kBacktrack: |
59 break; | 57 break; |
60 case ObjectGraph::Visitor::kAbort: | 58 case ObjectGraph::Visitor::kAbort: |
61 return; | 59 return; |
62 } | 60 } |
63 } | 61 } |
64 } | 62 } |
65 | 63 |
66 private: | 64 private: |
67 static RawObject* const kSentinel; | 65 static RawObject** const kSentinel; |
68 static const intptr_t kInitialCapacity = 1024; | 66 static const intptr_t kInitialCapacity = 1024; |
69 GrowableArray<RawObject*> data_; | 67 static const intptr_t kNoParent = -1; |
| 68 |
| 69 intptr_t Parent(intptr_t index) const { |
| 70 // The parent is just below the next sentinel. |
| 71 for (intptr_t i = index; i >= 1; --i) { |
| 72 if (data_[i] == kSentinel) { |
| 73 return i - 1; |
| 74 } |
| 75 } |
| 76 return kNoParent; |
| 77 } |
| 78 |
| 79 GrowableArray<RawObject**> data_; |
70 friend class StackIterator; | 80 friend class StackIterator; |
71 DISALLOW_COPY_AND_ASSIGN(Stack); | 81 DISALLOW_COPY_AND_ASSIGN(Stack); |
72 }; | 82 }; |
73 | 83 |
74 | 84 |
75 // We only visit heap objects, so any Smi works as the sentinel. | 85 RawObject** const ObjectGraph::Stack::kSentinel = NULL; |
76 RawObject* const ObjectGraph::Stack::kSentinel = Smi::New(0); | |
77 | 86 |
78 | 87 |
79 RawObject* ObjectGraph::StackIterator::Get() const { | 88 RawObject* ObjectGraph::StackIterator::Get() const { |
80 return stack_->data_[index_]; | 89 return *(stack_->data_[index_]); |
81 } | 90 } |
82 | 91 |
83 | 92 |
84 bool ObjectGraph::StackIterator::MoveToParent() { | 93 bool ObjectGraph::StackIterator::MoveToParent() { |
85 // The parent is just below the next sentinel. | 94 intptr_t parent = stack_->Parent(index_); |
86 for (int i = index_; i >= 1; --i) { | 95 if (parent == Stack::kNoParent) { |
87 if (stack_->data_[i] == Stack::kSentinel) { | 96 return false; |
88 index_ = i - 1; | 97 } else { |
89 return true; | 98 index_ = parent; |
90 } | 99 return true; |
91 } | 100 } |
92 return false; | 101 } |
| 102 |
| 103 |
| 104 intptr_t ObjectGraph::StackIterator::OffsetFromParentInWords() const { |
| 105 intptr_t parent = stack_->Parent(index_); |
| 106 if (parent == Stack::kNoParent) { |
| 107 return -1; |
| 108 } |
| 109 RawObject** parent_ptr = stack_->data_[parent]; |
| 110 uword parent_start = RawObject::ToAddr(*parent_ptr); |
| 111 RawObject** child_ptr = stack_->data_[index_]; |
| 112 uword child_ptr_addr = reinterpret_cast<uword>(child_ptr); |
| 113 intptr_t offset = child_ptr_addr - parent_start; |
| 114 ASSERT(offset > 0); |
| 115 ASSERT(offset < (*parent_ptr)->Size()); |
| 116 ASSERT(Utils::IsAligned(offset, kWordSize)); |
| 117 return offset >> kWordSizeLog2; |
93 } | 118 } |
94 | 119 |
95 | 120 |
96 class Unmarker : public ObjectVisitor { | 121 class Unmarker : public ObjectVisitor { |
97 public: | 122 public: |
98 explicit Unmarker(Isolate* isolate) : ObjectVisitor(isolate) { } | 123 explicit Unmarker(Isolate* isolate) : ObjectVisitor(isolate) { } |
99 | 124 |
100 void VisitObject(RawObject* obj) { | 125 void VisitObject(RawObject* obj) { |
101 if (obj->IsMarked()) { | 126 if (obj->IsMarked()) { |
102 obj->ClearMarkBit(); | 127 obj->ClearMarkBit(); |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
217 } | 242 } |
218 | 243 |
219 intptr_t length() const { return length_; } | 244 intptr_t length() const { return length_; } |
220 | 245 |
221 virtual Direction VisitObject(ObjectGraph::StackIterator* it) { | 246 virtual Direction VisitObject(ObjectGraph::StackIterator* it) { |
222 if (it->Get() != obj_) { | 247 if (it->Get() != obj_) { |
223 return kProceed; | 248 return kProceed; |
224 } else { | 249 } else { |
225 HANDLESCOPE(Isolate::Current()); | 250 HANDLESCOPE(Isolate::Current()); |
226 Object& current = Object::Handle(); | 251 Object& current = Object::Handle(); |
| 252 Smi& offset_from_parent = Smi::Handle(); |
227 do { | 253 do { |
228 if (!path_.IsNull() && length_ < path_.Length()) { | 254 intptr_t obj_index = length_ * 2; |
| 255 intptr_t offset_index = obj_index + 1; |
| 256 if (!path_.IsNull() && offset_index < path_.Length()) { |
229 current = it->Get(); | 257 current = it->Get(); |
230 path_.SetAt(length_, current); | 258 path_.SetAt(obj_index, current); |
| 259 offset_from_parent = Smi::New(it->OffsetFromParentInWords()); |
| 260 path_.SetAt(offset_index, offset_from_parent); |
231 } | 261 } |
232 ++length_; | 262 ++length_; |
233 } while (it->MoveToParent()); | 263 } while (it->MoveToParent()); |
234 return kAbort; | 264 return kAbort; |
235 } | 265 } |
236 } | 266 } |
237 | 267 |
238 private: | 268 private: |
239 RawObject* obj_; | 269 RawObject* obj_; |
240 const Array& path_; | 270 const Array& path_; |
241 intptr_t length_; | 271 intptr_t length_; |
242 }; | 272 }; |
243 | 273 |
244 | 274 |
245 intptr_t ObjectGraph::RetainingPath(Object* obj, const Array& path) { | 275 intptr_t ObjectGraph::RetainingPath(Object* obj, const Array& path) { |
246 NoGCScope no_gc_scope_; | 276 NoGCScope no_gc_scope_; |
247 // To break the trivial path, the handle 'obj' is temporarily cleared during | 277 // To break the trivial path, the handle 'obj' is temporarily cleared during |
248 // the search, but restored before returning. | 278 // the search, but restored before returning. |
249 RawObject* raw = obj->raw(); | 279 RawObject* raw = obj->raw(); |
250 *obj = Object::null(); | 280 *obj = Object::null(); |
251 RetainingPathVisitor visitor(raw, path); | 281 RetainingPathVisitor visitor(raw, path); |
252 IterateObjects(&visitor); | 282 IterateObjects(&visitor); |
253 *obj = raw; | 283 *obj = raw; |
254 return visitor.length(); | 284 return visitor.length(); |
255 } | 285 } |
256 | 286 |
257 } // namespace dart | 287 } // namespace dart |
OLD | NEW |