Skip to content

Commit 98da22a

Browse files
rmacnak-googlecommit-bot@chromium.org
authored andcommitted
[observatory] Display process-wide memory usage with heap snapshots.
Include major memory users known to the VM: isolate heaps, profiling buffers, timeline buffers. Change-Id: I2580ad74b5d4d07c5c75fde28bb7a5d71fddb09b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/127382 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com>
1 parent f024d9d commit 98da22a

File tree

9 files changed

+127
-0
lines changed

9 files changed

+127
-0
lines changed

runtime/observatory/lib/object_graph.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ class _ReadStream {
1717

1818
_ReadStream(this._buffer);
1919

20+
bool atEnd() => _position >= _buffer.length;
21+
2022
int readByte() => _buffer[_position++];
2123

2224
/// Read one ULEB128 number.
@@ -419,6 +421,8 @@ abstract class SnapshotGraph {
419421
Iterable<SnapshotClass> get classes;
420422
Iterable<SnapshotObject> get objects;
421423

424+
Map<String, int> get processPartitions;
425+
422426
factory SnapshotGraph(Uint8List encoded) => new _SnapshotGraph(encoded);
423427
Stream<String> process();
424428
}
@@ -496,6 +500,8 @@ class _SnapshotGraph implements SnapshotGraph {
496500
return result;
497501
}
498502

503+
final processPartitions = new Map<String, int>();
504+
499505
Stream<String> process() {
500506
final controller = new StreamController<String>.broadcast();
501507
(() async {
@@ -512,6 +518,9 @@ class _SnapshotGraph implements SnapshotGraph {
512518

513519
controller.add("Loading external properties...");
514520
await new Future(() => _readExternalProperties(stream));
521+
522+
controller.add("Loading process partitions...");
523+
await new Future(() => _readProcessPartitions(stream));
515524
stream = null;
516525

517526
controller.add("Compute class table...");
@@ -730,6 +739,17 @@ class _SnapshotGraph implements SnapshotGraph {
730739
_externalSizes = externalSizes;
731740
}
732741

742+
void _readProcessPartitions(_ReadStream stream) {
743+
// So it isn't null when loading older saved snapshots.
744+
processPartitions["RSS"] = 0;
745+
746+
while (!stream.atEnd()) {
747+
var name = stream.readUtf8();
748+
var size = stream.readUnsigned();
749+
processPartitions[name] = size;
750+
}
751+
}
752+
733753
void _computeClassTable() {
734754
var N = _N;
735755
var classes = _classes;

runtime/observatory/lib/src/elements/heap_snapshot.dart

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ enum HeapSnapshotTreeMode {
4141
classesTreeMap,
4242
successors,
4343
predecessors,
44+
process,
4445
}
4546

4647
class DominatorTreeMap extends TreeMap<SnapshotObject> {
@@ -116,6 +117,30 @@ class ClassesShallowTreeMap extends TreeMap<SnapshotClass> {
116117
}
117118
}
118119

120+
class ProcessTreeMap extends TreeMap<String> {
121+
static const String root = "RSS";
122+
HeapSnapshotElement element;
123+
M.HeapSnapshot snapshot;
124+
125+
ProcessTreeMap(this.element, this.snapshot);
126+
127+
int getSize(String node) => snapshot.processPartitions[node];
128+
String getType(String node) => node;
129+
String getLabel(String node) => node;
130+
String getParent(String node) => node == root ? null : root;
131+
Iterable<String> getChildren(String node) {
132+
if (node == root) {
133+
var children = snapshot.processPartitions.keys.toList();
134+
children.remove(root);
135+
return children;
136+
}
137+
return <String>[];
138+
}
139+
140+
void onSelect(String node) {}
141+
void onDetails(String node) {}
142+
}
143+
119144
// Using `null` to represent the root.
120145
class ClassesOwnershipTreeMap extends TreeMap<SnapshotClass> {
121146
HeapSnapshotElement element;
@@ -578,6 +603,27 @@ class HeapSnapshotElement extends CustomElement implements Renderable {
578603
..children = [content]
579604
]);
580605
break;
606+
case HeapSnapshotTreeMode.process:
607+
final content = new DivElement();
608+
content.style.border = '1px solid black';
609+
content.style.width = '100%';
610+
content.style.height = '100%';
611+
content.text = 'Performing layout...';
612+
Timer.run(() {
613+
// Generate the treemap after the content div has been added to the
614+
// document so that we can ask the browser how much space is
615+
// available for treemap layout.
616+
new ProcessTreeMap(this, _snapshot)
617+
.showIn(ProcessTreeMap.root, content);
618+
});
619+
report.addAll([
620+
new DivElement()
621+
..classes = ['content-centered-big']
622+
..style.width = '100%'
623+
..style.height = '100%'
624+
..children = [content]
625+
]);
626+
break;
581627
default:
582628
break;
583629
}
@@ -910,6 +956,8 @@ class HeapSnapshotElement extends CustomElement implements Renderable {
910956
return 'Successors / outgoing references';
911957
case HeapSnapshotTreeMode.predecessors:
912958
return 'Predecessors / incoming references';
959+
case HeapSnapshotTreeMode.process:
960+
return 'Process memory usage';
913961
}
914962
throw new Exception('Unknown HeapSnapshotTreeMode: $mode');
915963
}

runtime/observatory/lib/src/heap_snapshot/heap_snapshot.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class HeapSnapshot implements M.HeapSnapshot {
1515
HeapSnapshotMergedDominatorNode mergedDominatorTree;
1616
List<SnapshotClass> classes;
1717
SnapshotObject get root => graph.root;
18+
Map<String, int> get processPartitions => graph.processPartitions;
1819
Uint8List encoded;
1920

2021
Stream<String> loadProgress(S.Isolate isolate, Uint8List encoded) {

runtime/observatory/lib/src/models/objects/heap_snapshot.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ abstract class HeapSnapshot {
1010
SnapshotObject get root;
1111
HeapSnapshotMergedDominatorNode get mergedDominatorTree;
1212
Iterable<SnapshotClass> get classes;
13+
Map<String, int> get processPartitions;
1314
Uint8List get encoded;
1415
}
1516

runtime/vm/object_graph.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "vm/native_symbol.h"
1212
#include "vm/object.h"
1313
#include "vm/object_store.h"
14+
#include "vm/profiler.h"
1415
#include "vm/raw_object.h"
1516
#include "vm/raw_object_fields.h"
1617
#include "vm/reusable_handles.h"
@@ -1007,6 +1008,35 @@ void HeapSnapshotWriter::Write() {
10071008
isolate()->VisitWeakPersistentHandles(&visitor);
10081009
}
10091010

1011+
{
1012+
WriteUtf8("RSS");
1013+
WriteUnsigned(Service::CurrentRSS());
1014+
1015+
WriteUtf8("Dart Profiler Samples");
1016+
WriteUnsigned(Profiler::Size());
1017+
1018+
WriteUtf8("Dart Timeline Events");
1019+
WriteUnsigned(Timeline::recorder()->Size());
1020+
1021+
class WriteIsolateHeaps : public IsolateVisitor {
1022+
public:
1023+
explicit WriteIsolateHeaps(HeapSnapshotWriter* writer)
1024+
: writer_(writer) {}
1025+
1026+
virtual void VisitIsolate(Isolate* isolate) {
1027+
writer_->WriteUtf8(isolate->name());
1028+
writer_->WriteUnsigned((isolate->heap()->TotalCapacityInWords() +
1029+
isolate->heap()->TotalExternalInWords()) *
1030+
kWordSize);
1031+
}
1032+
1033+
private:
1034+
HeapSnapshotWriter* const writer_;
1035+
};
1036+
WriteIsolateHeaps visitor(this);
1037+
Isolate::VisitIsolates(&visitor);
1038+
}
1039+
10101040
ClearObjectIds();
10111041
Flush(true);
10121042
}

runtime/vm/profiler.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class Profiler : public AllStatic {
9696
// Copies the counter values.
9797
return counters_;
9898
}
99+
inline static intptr_t Size();
99100

100101
private:
101102
static void DumpStackTrace(uword sp, uword fp, uword pc, bool for_crash);
@@ -674,6 +675,8 @@ class SampleBuffer {
674675

675676
ProcessedSampleBuffer* BuildProcessedSampleBuffer(SampleFilter* filter);
676677

678+
intptr_t Size() { return memory_->size(); }
679+
677680
protected:
678681
ProcessedSample* BuildProcessedSample(Sample* sample,
679682
const CodeLookupTable& clt);
@@ -705,6 +708,17 @@ class AllocationSampleBuffer : public SampleBuffer {
705708
DISALLOW_COPY_AND_ASSIGN(AllocationSampleBuffer);
706709
};
707710

711+
intptr_t Profiler::Size() {
712+
intptr_t size = 0;
713+
if (sample_buffer_ != nullptr) {
714+
size += sample_buffer_->Size();
715+
}
716+
if (allocation_sample_buffer_ != nullptr) {
717+
size += allocation_sample_buffer_->Size();
718+
}
719+
return size;
720+
}
721+
708722
// A |ProcessedSample| is a combination of 1 (or more) |Sample|(s) that have
709723
// been merged into a logical sample. The raw data may have been processed to
710724
// improve the quality of the stack trace.

runtime/vm/timeline.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,10 @@ TimelineEventFixedBufferRecorder::~TimelineEventFixedBufferRecorder() {
11611161
delete memory_;
11621162
}
11631163

1164+
intptr_t TimelineEventFixedBufferRecorder::Size() {
1165+
return memory_->size();
1166+
}
1167+
11641168
#ifndef PRODUCT
11651169
void TimelineEventFixedBufferRecorder::PrintJSONEvents(
11661170
JSONArray* events,

runtime/vm/timeline.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,8 @@ class TimelineEventRecorder {
719719

720720
void FinishBlock(TimelineEventBlock* block);
721721

722+
virtual intptr_t Size() = 0;
723+
722724
protected:
723725
#ifndef PRODUCT
724726
void WriteTo(const char* directory);
@@ -771,6 +773,8 @@ class TimelineEventFixedBufferRecorder : public TimelineEventRecorder {
771773
void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter);
772774
#endif
773775

776+
intptr_t Size();
777+
774778
protected:
775779
TimelineEvent* StartEvent();
776780
void CompleteEvent(TimelineEvent* event);
@@ -857,6 +861,7 @@ class TimelineEventEndlessRecorder : public TimelineEventRecorder {
857861
#endif
858862

859863
const char* name() const { return ENDLESS_RECORDER_NAME; }
864+
intptr_t Size() { return block_index_ * sizeof(TimelineEventBlock); }
860865

861866
protected:
862867
TimelineEvent* StartEvent();
@@ -929,6 +934,7 @@ class TimelineEventFuchsiaRecorder : public TimelineEventPlatformRecorder {
929934
virtual ~TimelineEventFuchsiaRecorder() {}
930935

931936
const char* name() const { return FUCHSIA_RECORDER_NAME; }
937+
intptr_t Size() { return 0; }
932938

933939
private:
934940
void OnEvent(TimelineEvent* event);
@@ -949,6 +955,7 @@ class TimelineEventSystraceRecorder : public TimelineEventPlatformRecorder {
949955
intptr_t buffer_size);
950956

951957
const char* name() const { return SYSTRACE_RECORDER_NAME; }
958+
intptr_t Size() { return 0; }
952959

953960
private:
954961
void OnEvent(TimelineEvent* event);

runtime/vm/timeline_test.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ class EventCounterRecorder : public TimelineEventCallbackRecorder {
275275

276276
intptr_t CountFor(TimelineEvent::EventType type) { return counts_[type]; }
277277

278+
intptr_t Size() { return -1; }
279+
278280
private:
279281
intptr_t counts_[TimelineEvent::kNumEventTypes];
280282
};

0 commit comments

Comments
 (0)