blob: 55dc8159b803a08b334f60b6a6bf54657871b22d [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include <memory>
#include "vm/allocation.h"
#include "vm/dart_api_state.h"
#include "vm/thread_stack_resource.h"
namespace dart {
class Array;
class Object;
class CountingPage;
#if !defined(PRODUCT)
// Utility to traverse the object graph in an ordered fashion.
// Example uses:
// - find a retaining path from the isolate roots to a particular object, or
// - determine how much memory is retained by some particular object(s).
class ObjectGraph : public ThreadStackResource {
class Stack;
// Allows climbing the search tree all the way to the root.
class StackIterator {
// The object this iterator currently points to.
ObjectPtr Get() const;
// Returns false if there is no parent.
bool MoveToParent();
// Offset into parent for the pointer to current object. -1 if no parent.
intptr_t OffsetFromParent() const;
StackIterator(const Stack* stack, intptr_t index)
: stack_(stack), index_(index) {}
const Stack* stack_;
intptr_t index_;
friend class ObjectGraph::Stack;
class Visitor {
// Directs how the search should continue after visiting an object.
enum Direction {
kProceed, // Recurse on this object's pointers.
kBacktrack, // Ignore this object's pointers.
kAbort, // Terminate the entire search immediately.
virtual ~Visitor() {}
// Visits the object pointed to by *it. The iterator is only valid
// during this call. This method must not allocate from the heap or
// trigger GC in any way.
virtual Direction VisitObject(StackIterator* it) = 0;
virtual bool visit_weak_persistent_handles() const { return false; }
const char* gc_root_type = NULL;
bool is_traversing = false;
typedef struct {
intptr_t length;
const char* gc_root_type;
} RetainingPathResult;
explicit ObjectGraph(Thread* thread);
// Visits all strongly reachable objects in the isolate's heap, in a
// pre-order, depth first traversal.
void IterateObjects(Visitor* visitor);
void IterateUserObjects(Visitor* visitor);
// Like 'IterateObjects', but restricted to objects reachable from 'root'
// (including 'root' itself).
void IterateObjectsFrom(const Object& root, Visitor* visitor);
void IterateObjectsFrom(intptr_t class_id,
HeapIterationScope* iteration,
Visitor* visitor);
// The number of bytes retained by 'obj'.
intptr_t SizeRetainedByInstance(const Object& obj);
intptr_t SizeReachableByInstance(const Object& obj);
// The number of bytes retained by the set of all objects of the given class.
intptr_t SizeRetainedByClass(intptr_t class_id);
intptr_t SizeReachableByClass(intptr_t class_id);
// Finds some retaining path from the isolate roots to 'obj'. Populates the
// provided array with pairs of (object, offset from parent in words),
// starting with 'obj' itself, as far as there is room. Returns the number
// of objects on the full path. A null input array behaves like a zero-length
// input array. The 'offset' of a root is -1.
// To break the trivial path, the handle 'obj' is temporarily cleared during
// the search, but restored before returning. If no path is found (i.e., the
// provided handle was the only way to reach the object), zero is returned.
RetainingPathResult RetainingPath(Object* obj, const Array& path);
// Find the objects that reference 'obj'. Populates the provided array with
// pairs of (object pointing to 'obj', offset of pointer in words), as far as
// there is room. Returns the number of objects found.
// An object for which this function answers no inbound references might still
// be live due to references from the stack or embedder handles.
intptr_t InboundReferences(Object* obj, const Array& references);
// Generates a dump of the heap, whose format is described in
// runtime/vm/service/
class HeapSnapshotWriter : public ThreadStackResource {
explicit HeapSnapshotWriter(Thread* thread) : ThreadStackResource(thread) {}
void WriteSigned(int64_t value) {
EnsureAvailable((sizeof(value) * kBitsPerByte) / 7 + 1);
bool is_last_part = false;
while (!is_last_part) {
uint8_t part = value & 0x7F;
value >>= 7;
if ((value == 0 && (part & 0x40) == 0) ||
(value == static_cast<intptr_t>(-1) && (part & 0x40) != 0)) {
is_last_part = true;
} else {
part |= 0x80;
buffer_[size_++] = part;
void WriteUnsigned(uintptr_t value) {
EnsureAvailable((sizeof(value) * kBitsPerByte) / 7 + 1);
bool is_last_part = false;
while (!is_last_part) {
uint8_t part = value & 0x7F;
value >>= 7;
if (value == 0) {
is_last_part = true;
} else {
part |= 0x80;
buffer_[size_++] = part;
void WriteBytes(const void* bytes, intptr_t len) {
memmove(&buffer_[size_], bytes, len);
size_ += len;
void ScrubAndWriteUtf8(char* value) {
intptr_t len = strlen(value);
for (intptr_t i = len - 1; i >= 0; i--) {
if (value[i] == '@') {
value[i] = '\0';
void WriteUtf8(const char* value) {
intptr_t len = strlen(value);
WriteBytes(value, len);
void AssignObjectId(ObjectPtr obj);
intptr_t GetObjectId(ObjectPtr obj) const;
void ClearObjectIds();
void CountReferences(intptr_t count);
void CountExternalProperty();
void Write();
static uint32_t GetHeapSnapshotIdentityHash(Thread* thread, ObjectPtr obj);
static uint32_t GetHashHelper(Thread* thread, ObjectPtr obj);
static const intptr_t kMetadataReservation = 512;
static const intptr_t kPreferredChunkSize = MB;
void SetupCountingPages();
bool OnImagePage(ObjectPtr obj) const;
CountingPage* FindCountingPage(ObjectPtr obj) const;
void EnsureAvailable(intptr_t needed);
void Flush(bool last = false);
uint8_t* buffer_ = nullptr;
intptr_t size_ = 0;
intptr_t capacity_ = 0;
intptr_t class_count_ = 0;
intptr_t object_count_ = 0;
intptr_t reference_count_ = 0;
intptr_t external_property_count_ = 0;
struct ImagePageRange {
uword base;
uword size;
// There are up to 4 images to consider:
// {instructions, data} x {vm isolate, current isolate}
static const intptr_t kMaxImagePages = 4;
ImagePageRange image_page_ranges_[kMaxImagePages];
class CountObjectsVisitor : public ObjectVisitor, public HandleVisitor {
CountObjectsVisitor(Thread* thread, intptr_t class_count);
~CountObjectsVisitor() {}
void VisitObject(ObjectPtr obj);
void VisitHandle(uword addr);
std::unique_ptr<intptr_t[]> new_count_;
std::unique_ptr<intptr_t[]> new_size_;
std::unique_ptr<intptr_t[]> new_external_size_;
std::unique_ptr<intptr_t[]> old_count_;
std::unique_ptr<intptr_t[]> old_size_;
std::unique_ptr<intptr_t[]> old_external_size_;
#endif // !defined(PRODUCT)
} // namespace dart