| 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/reusable_handles.h" | 12 #include "vm/reusable_handles.h" |
| 13 #include "vm/visitor.h" | 13 #include "vm/visitor.h" |
| 14 | 14 |
| 15 namespace dart { | 15 namespace dart { |
| 16 | 16 |
| 17 // The state of a pre-order, depth-first traversal of an object graph. | 17 // The state of a pre-order, depth-first traversal of an object graph. |
| 18 // When a node is visited, *all* its children are pushed to the stack at once. | 18 // When a node is visited, *all* its children are pushed to the stack at once. |
| 19 // We insert a sentinel between the node and its children on the stack, to | 19 // We insert a sentinel between the node and its children on the stack, to |
| 20 // remember that the node has been visited. The node is kept on the stack while | 20 // remember that the node has been visited. The node is kept on the stack while |
| 21 // its children are processed, to give the visitor a complete chain of parents. | 21 // its children are processed, to give the visitor a complete chain of parents. |
| 22 // | 22 // |
| 23 // TODO(koda): Potential optimizations: | 23 // TODO(koda): Potential optimizations: |
| 24 // - Use tag bits for compact Node and sentinel representations. | 24 // - Use tag bits for compact Node and sentinel representations. |
| 25 class ObjectGraph::Stack : public ObjectPointerVisitor { | 25 class ObjectGraph::Stack : public ObjectPointerVisitor { |
| 26 public: | 26 public: |
| 27 explicit Stack(Isolate* isolate) | 27 explicit Stack(Isolate* isolate) |
| 28 : ObjectPointerVisitor(isolate), data_(kInitialCapacity) { } | 28 : ObjectPointerVisitor(isolate), data_(kInitialCapacity) {} |
| 29 | 29 |
| 30 // Marks and pushes. Used to initialize this stack with roots. | 30 // Marks and pushes. Used to initialize this stack with roots. |
| 31 virtual void VisitPointers(RawObject** first, RawObject** last) { | 31 virtual void VisitPointers(RawObject** first, RawObject** last) { |
| 32 for (RawObject** current = first; current <= last; ++current) { | 32 for (RawObject** current = first; current <= last; ++current) { |
| 33 if ((*current)->IsHeapObject() && !(*current)->IsMarked()) { | 33 if ((*current)->IsHeapObject() && !(*current)->IsMarked()) { |
| 34 (*current)->SetMarkBit(); | 34 (*current)->SetMarkBit(); |
| 35 Node node; | 35 Node node; |
| 36 node.ptr = current; | 36 node.ptr = current; |
| 37 node.obj = *current; | 37 node.obj = *current; |
| 38 data_.Add(node); | 38 data_.Add(node); |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 131 // Some internal VM objects visit pointers not contained within the parent. | 131 // Some internal VM objects visit pointers not contained within the parent. |
| 132 // For instance, RawCode::VisitCodePointers visits pointers in instructions. | 132 // For instance, RawCode::VisitCodePointers visits pointers in instructions. |
| 133 ASSERT(!parent.obj->IsDartInstance()); | 133 ASSERT(!parent.obj->IsDartInstance()); |
| 134 return -1; | 134 return -1; |
| 135 } | 135 } |
| 136 } | 136 } |
| 137 | 137 |
| 138 | 138 |
| 139 class Unmarker : public ObjectVisitor { | 139 class Unmarker : public ObjectVisitor { |
| 140 public: | 140 public: |
| 141 Unmarker() { } | 141 Unmarker() {} |
| 142 | 142 |
| 143 void VisitObject(RawObject* obj) { | 143 void VisitObject(RawObject* obj) { |
| 144 if (obj->IsMarked()) { | 144 if (obj->IsMarked()) { |
| 145 obj->ClearMarkBit(); | 145 obj->ClearMarkBit(); |
| 146 } | 146 } |
| 147 } | 147 } |
| 148 | 148 |
| 149 static void UnmarkAll(Isolate* isolate) { | 149 static void UnmarkAll(Isolate* isolate) { |
| 150 Unmarker unmarker; | 150 Unmarker unmarker; |
| 151 isolate->heap()->IterateObjects(&unmarker); | 151 isolate->heap()->IterateObjects(&unmarker); |
| 152 } | 152 } |
| 153 | 153 |
| 154 private: | 154 private: |
| 155 DISALLOW_COPY_AND_ASSIGN(Unmarker); | 155 DISALLOW_COPY_AND_ASSIGN(Unmarker); |
| 156 }; | 156 }; |
| 157 | 157 |
| 158 | 158 |
| 159 ObjectGraph::ObjectGraph(Thread* thread) | 159 ObjectGraph::ObjectGraph(Thread* thread) : StackResource(thread) { |
| 160 : StackResource(thread) { | |
| 161 // The VM isolate has all its objects pre-marked, so iterating over it | 160 // The VM isolate has all its objects pre-marked, so iterating over it |
| 162 // would be a no-op. | 161 // would be a no-op. |
| 163 ASSERT(thread->isolate() != Dart::vm_isolate()); | 162 ASSERT(thread->isolate() != Dart::vm_isolate()); |
| 164 thread->isolate()->heap()->WriteProtectCode(false); | 163 thread->isolate()->heap()->WriteProtectCode(false); |
| 165 } | 164 } |
| 166 | 165 |
| 167 | 166 |
| 168 ObjectGraph::~ObjectGraph() { | 167 ObjectGraph::~ObjectGraph() { |
| 169 isolate()->heap()->WriteProtectCode(true); | 168 isolate()->heap()->WriteProtectCode(true); |
| 170 } | 169 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 186 RawObject* root_raw = root.raw(); | 185 RawObject* root_raw = root.raw(); |
| 187 stack.VisitPointer(&root_raw); | 186 stack.VisitPointer(&root_raw); |
| 188 stack.TraverseGraph(visitor); | 187 stack.TraverseGraph(visitor); |
| 189 Unmarker::UnmarkAll(isolate()); | 188 Unmarker::UnmarkAll(isolate()); |
| 190 } | 189 } |
| 191 | 190 |
| 192 | 191 |
| 193 class InstanceAccumulator : public ObjectVisitor { | 192 class InstanceAccumulator : public ObjectVisitor { |
| 194 public: | 193 public: |
| 195 InstanceAccumulator(ObjectGraph::Stack* stack, intptr_t class_id) | 194 InstanceAccumulator(ObjectGraph::Stack* stack, intptr_t class_id) |
| 196 : stack_(stack), class_id_(class_id) { } | 195 : stack_(stack), class_id_(class_id) {} |
| 197 | 196 |
| 198 void VisitObject(RawObject* obj) { | 197 void VisitObject(RawObject* obj) { |
| 199 if (obj->GetClassId() == class_id_) { | 198 if (obj->GetClassId() == class_id_) { |
| 200 RawObject* rawobj = obj; | 199 RawObject* rawobj = obj; |
| 201 stack_->VisitPointer(&rawobj); | 200 stack_->VisitPointer(&rawobj); |
| 202 } | 201 } |
| 203 } | 202 } |
| 204 | 203 |
| 205 private: | 204 private: |
| 206 ObjectGraph::Stack* stack_; | 205 ObjectGraph::Stack* stack_; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 218 InstanceAccumulator accumulator(&stack, class_id); | 217 InstanceAccumulator accumulator(&stack, class_id); |
| 219 isolate()->heap()->IterateObjects(&accumulator); | 218 isolate()->heap()->IterateObjects(&accumulator); |
| 220 | 219 |
| 221 stack.TraverseGraph(visitor); | 220 stack.TraverseGraph(visitor); |
| 222 Unmarker::UnmarkAll(isolate()); | 221 Unmarker::UnmarkAll(isolate()); |
| 223 } | 222 } |
| 224 | 223 |
| 225 | 224 |
| 226 class SizeVisitor : public ObjectGraph::Visitor { | 225 class SizeVisitor : public ObjectGraph::Visitor { |
| 227 public: | 226 public: |
| 228 SizeVisitor() : size_(0) { } | 227 SizeVisitor() : size_(0) {} |
| 229 intptr_t size() const { return size_; } | 228 intptr_t size() const { return size_; } |
| 230 virtual bool ShouldSkip(RawObject* obj) const { return false; } | 229 virtual bool ShouldSkip(RawObject* obj) const { return false; } |
| 231 virtual Direction VisitObject(ObjectGraph::StackIterator* it) { | 230 virtual Direction VisitObject(ObjectGraph::StackIterator* it) { |
| 232 RawObject* obj = it->Get(); | 231 RawObject* obj = it->Get(); |
| 233 if (ShouldSkip(obj)) { | 232 if (ShouldSkip(obj)) { |
| 234 return kBacktrack; | 233 return kBacktrack; |
| 235 } | 234 } |
| 236 size_ += obj->Size(); | 235 size_ += obj->Size(); |
| 237 return kProceed; | 236 return kProceed; |
| 238 } | 237 } |
| 238 |
| 239 private: | 239 private: |
| 240 intptr_t size_; | 240 intptr_t size_; |
| 241 }; | 241 }; |
| 242 | 242 |
| 243 | 243 |
| 244 class SizeExcludingObjectVisitor : public SizeVisitor { | 244 class SizeExcludingObjectVisitor : public SizeVisitor { |
| 245 public: | 245 public: |
| 246 explicit SizeExcludingObjectVisitor(const Object& skip) : skip_(skip) { } | 246 explicit SizeExcludingObjectVisitor(const Object& skip) : skip_(skip) {} |
| 247 virtual bool ShouldSkip(RawObject* obj) const { return obj == skip_.raw(); } | 247 virtual bool ShouldSkip(RawObject* obj) const { return obj == skip_.raw(); } |
| 248 |
| 248 private: | 249 private: |
| 249 const Object& skip_; | 250 const Object& skip_; |
| 250 }; | 251 }; |
| 251 | 252 |
| 252 | 253 |
| 253 class SizeExcludingClassVisitor : public SizeVisitor { | 254 class SizeExcludingClassVisitor : public SizeVisitor { |
| 254 public: | 255 public: |
| 255 explicit SizeExcludingClassVisitor(intptr_t skip) : skip_(skip) { } | 256 explicit SizeExcludingClassVisitor(intptr_t skip) : skip_(skip) {} |
| 256 virtual bool ShouldSkip(RawObject* obj) const { | 257 virtual bool ShouldSkip(RawObject* obj) const { |
| 257 return obj->GetClassId() == skip_; | 258 return obj->GetClassId() == skip_; |
| 258 } | 259 } |
| 260 |
| 259 private: | 261 private: |
| 260 const intptr_t skip_; | 262 const intptr_t skip_; |
| 261 }; | 263 }; |
| 262 | 264 |
| 263 | 265 |
| 264 intptr_t ObjectGraph::SizeRetainedByInstance(const Object& obj) { | 266 intptr_t ObjectGraph::SizeRetainedByInstance(const Object& obj) { |
| 265 SizeVisitor total; | 267 SizeVisitor total; |
| 266 IterateObjects(&total); | 268 IterateObjects(&total); |
| 267 intptr_t size_total = total.size(); | 269 intptr_t size_total = total.size(); |
| 268 SizeExcludingObjectVisitor excluding_obj(obj); | 270 SizeExcludingObjectVisitor excluding_obj(obj); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 305 ASSERT(Thread::Current()->no_safepoint_scope_depth() != 0); | 307 ASSERT(Thread::Current()->no_safepoint_scope_depth() != 0); |
| 306 } | 308 } |
| 307 | 309 |
| 308 intptr_t length() const { return length_; } | 310 intptr_t length() const { return length_; } |
| 309 | 311 |
| 310 bool ShouldSkip(RawObject* obj) { | 312 bool ShouldSkip(RawObject* obj) { |
| 311 // A retaining path through ICData is never the only retaining path, | 313 // A retaining path through ICData is never the only retaining path, |
| 312 // and it is less informative than its alternatives. | 314 // and it is less informative than its alternatives. |
| 313 intptr_t cid = obj->GetClassId(); | 315 intptr_t cid = obj->GetClassId(); |
| 314 switch (cid) { | 316 switch (cid) { |
| 315 case kICDataCid: | 317 case kICDataCid: |
| 316 return true; | 318 return true; |
| 317 default: | 319 default: |
| 318 return false; | 320 return false; |
| 319 } | 321 } |
| 320 } | 322 } |
| 321 | 323 |
| 322 bool ShouldStop(RawObject* obj) { | 324 bool ShouldStop(RawObject* obj) { |
| 323 // A static field is considered a root from a language point of view. | 325 // A static field is considered a root from a language point of view. |
| 324 if (obj->IsField()) { | 326 if (obj->IsField()) { |
| 325 const Field& field = Field::Handle(static_cast<RawField*>(obj)); | 327 const Field& field = Field::Handle(static_cast<RawField*>(obj)); |
| 326 return field.is_static(); | 328 return field.is_static(); |
| 327 } | 329 } |
| 328 return false; | 330 return false; |
| 329 } | 331 } |
| 330 | 332 |
| 331 void StartList() { | 333 void StartList() { was_last_array_ = false; } |
| 332 was_last_array_ = false; | |
| 333 } | |
| 334 | 334 |
| 335 intptr_t HideNDescendant(RawObject* obj) { | 335 intptr_t HideNDescendant(RawObject* obj) { |
| 336 // A GrowableObjectArray overwrites its internal storage. | 336 // A GrowableObjectArray overwrites its internal storage. |
| 337 // Keeping both of them in the list is redundant. | 337 // Keeping both of them in the list is redundant. |
| 338 if (was_last_array_ && obj->IsGrowableObjectArray()) { | 338 if (was_last_array_ && obj->IsGrowableObjectArray()) { |
| 339 was_last_array_ = false; | 339 was_last_array_ = false; |
| 340 return 1; | 340 return 1; |
| 341 } | 341 } |
| 342 // A LinkedHasMap overwrites its internal storage. | 342 // A LinkedHasMap overwrites its internal storage. |
| 343 // Keeping both of them in the list is redundant. | 343 // Keeping both of them in the list is redundant. |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 401 | 401 |
| 402 | 402 |
| 403 class InboundReferencesVisitor : public ObjectVisitor, | 403 class InboundReferencesVisitor : public ObjectVisitor, |
| 404 public ObjectPointerVisitor { | 404 public ObjectPointerVisitor { |
| 405 public: | 405 public: |
| 406 // We cannot use a GrowableObjectArray, since we must not trigger GC. | 406 // We cannot use a GrowableObjectArray, since we must not trigger GC. |
| 407 InboundReferencesVisitor(Isolate* isolate, | 407 InboundReferencesVisitor(Isolate* isolate, |
| 408 RawObject* target, | 408 RawObject* target, |
| 409 const Array& references, | 409 const Array& references, |
| 410 Object* scratch) | 410 Object* scratch) |
| 411 : ObjectPointerVisitor(isolate), source_(NULL), | 411 : ObjectPointerVisitor(isolate), |
| 412 target_(target), references_(references), scratch_(scratch), length_(0) { | 412 source_(NULL), |
| 413 target_(target), |
| 414 references_(references), |
| 415 scratch_(scratch), |
| 416 length_(0) { |
| 413 ASSERT(Thread::Current()->no_safepoint_scope_depth() != 0); | 417 ASSERT(Thread::Current()->no_safepoint_scope_depth() != 0); |
| 414 } | 418 } |
| 415 | 419 |
| 416 intptr_t length() const { return length_; } | 420 intptr_t length() const { return length_; } |
| 417 | 421 |
| 418 virtual void VisitObject(RawObject* raw_obj) { | 422 virtual void VisitObject(RawObject* raw_obj) { |
| 419 source_ = raw_obj; | 423 source_ = raw_obj; |
| 420 raw_obj->VisitPointers(this); | 424 raw_obj->VisitPointers(this); |
| 421 } | 425 } |
| 422 | 426 |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 499 } | 503 } |
| 500 | 504 |
| 501 intptr_t count() const { return count_; } | 505 intptr_t count() const { return count_; } |
| 502 | 506 |
| 503 private: | 507 private: |
| 504 WriteStream* stream_; | 508 WriteStream* stream_; |
| 505 intptr_t count_; | 509 intptr_t count_; |
| 506 }; | 510 }; |
| 507 | 511 |
| 508 | 512 |
| 509 static void WriteHeader(RawObject* raw, intptr_t size, intptr_t cid, | 513 static void WriteHeader(RawObject* raw, |
| 510 WriteStream* stream) { | 514 intptr_t size, |
| 515 intptr_t cid, |
| 516 WriteStream* stream) { |
| 511 WritePtr(raw, stream); | 517 WritePtr(raw, stream); |
| 512 ASSERT(Utils::IsAligned(size, kObjectAlignment)); | 518 ASSERT(Utils::IsAligned(size, kObjectAlignment)); |
| 513 stream->WriteUnsigned(size); | 519 stream->WriteUnsigned(size); |
| 514 stream->WriteUnsigned(cid); | 520 stream->WriteUnsigned(cid); |
| 515 } | 521 } |
| 516 | 522 |
| 517 | 523 |
| 518 class WriteGraphVisitor : public ObjectGraph::Visitor { | 524 class WriteGraphVisitor : public ObjectGraph::Visitor { |
| 519 public: | 525 public: |
| 520 WriteGraphVisitor(Isolate* isolate, WriteStream* stream) | 526 WriteGraphVisitor(Isolate* isolate, WriteStream* stream) |
| 521 : stream_(stream), ptr_writer_(isolate, stream), count_(0) {} | 527 : stream_(stream), ptr_writer_(isolate, stream), count_(0) {} |
| 522 | 528 |
| 523 virtual Direction VisitObject(ObjectGraph::StackIterator* it) { | 529 virtual Direction VisitObject(ObjectGraph::StackIterator* it) { |
| 524 RawObject* raw_obj = it->Get(); | 530 RawObject* raw_obj = it->Get(); |
| 525 Thread* thread = Thread::Current(); | 531 Thread* thread = Thread::Current(); |
| 526 REUSABLE_OBJECT_HANDLESCOPE(thread); | 532 REUSABLE_OBJECT_HANDLESCOPE(thread); |
| 527 Object& obj = thread->ObjectHandle(); | 533 Object& obj = thread->ObjectHandle(); |
| 528 obj = raw_obj; | 534 obj = raw_obj; |
| 529 // Each object is a header + a zero-terminated list of its neighbors. | 535 // Each object is a header + a zero-terminated list of its neighbors. |
| 530 WriteHeader(raw_obj, raw_obj->Size(), obj.GetClassId(), stream_); | 536 WriteHeader(raw_obj, raw_obj->Size(), obj.GetClassId(), stream_); |
| 531 raw_obj->VisitPointers(&ptr_writer_); | 537 raw_obj->VisitPointers(&ptr_writer_); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 558 { | 564 { |
| 559 WritePointerVisitor ptr_writer(isolate(), stream); | 565 WritePointerVisitor ptr_writer(isolate(), stream); |
| 560 isolate()->IterateObjectPointers(&ptr_writer, false); | 566 isolate()->IterateObjectPointers(&ptr_writer, false); |
| 561 } | 567 } |
| 562 stream->WriteUnsigned(0); | 568 stream->WriteUnsigned(0); |
| 563 IterateObjects(&visitor); | 569 IterateObjects(&visitor); |
| 564 return visitor.count() + 1; // + root | 570 return visitor.count() + 1; // + root |
| 565 } | 571 } |
| 566 | 572 |
| 567 } // namespace dart | 573 } // namespace dart |
| OLD | NEW |