mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
![Ryan Macnak](/assets/img/avatar_default.png)
This replaces _requestHeapSnapshot and _getObjectByAddresses, which relied on the invalid assumption that objects don't move and the heap hasn't mutated since the initial snapshot. Updates Observatory to the new format. Adds treemap visualization of the dominator tree. Adds the ability to save and load heap snapshots to files. Removes analysis grouping references by source and target class. Removes ability to navigate from a pixel in the fragmentation view to inspector. Removes ability to query the top retaining instance from an ordinary class browser. Change-Id: Ia7781c05d43bf3ec149f8b4ecab803b37c3ee981 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/112181 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com>
208 lines
6.3 KiB
C++
208 lines
6.3 KiB
C++
// 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.
|
|
|
|
#ifndef RUNTIME_VM_OBJECT_GRAPH_H_
|
|
#define RUNTIME_VM_OBJECT_GRAPH_H_
|
|
|
|
#include "vm/allocation.h"
|
|
#include "vm/thread_stack_resource.h"
|
|
|
|
namespace dart {
|
|
|
|
class Array;
|
|
class Object;
|
|
class RawObject;
|
|
|
|
#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 {
|
|
public:
|
|
class Stack;
|
|
|
|
// Allows climbing the search tree all the way to the root.
|
|
class StackIterator {
|
|
public:
|
|
// The object this iterator currently points to.
|
|
RawObject* 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 OffsetFromParentInWords() const;
|
|
|
|
private:
|
|
StackIterator(const Stack* stack, intptr_t index)
|
|
: stack_(stack), index_(index) {}
|
|
const Stack* stack_;
|
|
intptr_t index_;
|
|
friend class ObjectGraph::Stack;
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(StackIterator);
|
|
};
|
|
|
|
class Visitor {
|
|
public:
|
|
// 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);
|
|
~ObjectGraph();
|
|
|
|
// 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, 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);
|
|
|
|
private:
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(ObjectGraph);
|
|
};
|
|
|
|
// Generates a dump of the heap, whose format is described in
|
|
// runtime/vm/service/heap_snapshot.md.
|
|
class HeapSnapshotWriter : public ThreadStackResource {
|
|
public:
|
|
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) {
|
|
EnsureAvailable(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';
|
|
}
|
|
}
|
|
WriteUtf8(value);
|
|
}
|
|
|
|
void WriteUtf8(const char* value) {
|
|
intptr_t len = strlen(value);
|
|
WriteUnsigned(len);
|
|
WriteBytes(value, len);
|
|
}
|
|
|
|
void AssignObjectId(RawObject* obj);
|
|
intptr_t GetObjectId(RawObject* obj);
|
|
void ClearObjectIds();
|
|
void CountReferences(intptr_t count);
|
|
void CountExternalProperty();
|
|
|
|
void Write();
|
|
|
|
private:
|
|
static const intptr_t kMetadataReservation = 512;
|
|
static const intptr_t kPreferredChunkSize = MB;
|
|
|
|
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;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(HeapSnapshotWriter);
|
|
};
|
|
|
|
#endif // !defined(PRODUCT)
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_OBJECT_GRAPH_H_
|