Make the compactor invokable from Observatory's heap map page.

Add GCReason to timeline events.

Bug: https://github.com/dart-lang/sdk/issues/30978
Change-Id: Ie4514ca3fb0fa6197a895e54618dfcba1dfe3a8d
Reviewed-on: https://dart-review.googlesource.com/25120
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
This commit is contained in:
Ryan Macnak 2017-11-30 22:55:56 +00:00 committed by commit-bot@chromium.org
parent 10ec335464
commit 104336699d
7 changed files with 83 additions and 35 deletions

View file

@ -108,8 +108,12 @@ class HeapMapElement extends HtmlElement implements Renderable {
new NavVMMenuElement(_vm, _events, queue: _r.queue), new NavVMMenuElement(_vm, _events, queue: _r.queue),
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue), new NavIsolateMenuElement(_isolate, _events, queue: _r.queue),
navMenu('heap map'), navMenu('heap map'),
new NavRefreshElement(label: 'GC', queue: _r.queue) new NavRefreshElement(label: 'Mark-Compact', queue: _r.queue)
..onRefresh.listen((_) => _refresh(gc: true)), ..onRefresh.listen((_) => _refresh(gc: "mark-compact")),
new NavRefreshElement(label: 'Mark-Sweep', queue: _r.queue)
..onRefresh.listen((_) => _refresh(gc: "mark-sweep")),
new NavRefreshElement(label: 'Scavenge', queue: _r.queue)
..onRefresh.listen((_) => _refresh(gc: "scavenge")),
new NavRefreshElement(queue: _r.queue) new NavRefreshElement(queue: _r.queue)
..onRefresh.listen((_) => _refresh()), ..onRefresh.listen((_) => _refresh()),
new NavNotifyElement(_notifications, queue: _r.queue) new NavNotifyElement(_notifications, queue: _r.queue)
@ -279,11 +283,11 @@ class HeapMapElement extends HtmlElement implements Renderable {
}); });
} }
Future _refresh({gc: false}) { Future _refresh({String gc}) {
final isolate = _isolate as S.Isolate; final isolate = _isolate as S.Isolate;
var params = {}; var params = {};
if (gc) { if (gc != null) {
params['gc'] = 'full'; params['gc'] = gc;
} }
return isolate return isolate
.invokeRpc('_getHeapMap', params) .invokeRpc('_getHeapMap', params)

View file

@ -22,6 +22,45 @@ var tests = [
expect(result['pages'][0]['objects'].length, isPositive); expect(result['pages'][0]['objects'].length, isPositive);
expect(result['pages'][0]['objects'][0], isPositive); expect(result['pages'][0]['objects'][0], isPositive);
}, },
(Isolate isolate) async {
var params = {'gc': 'scavenge'};
var result = await isolate.invokeRpcNoUpgrade('_getHeapMap', params);
expect(result['type'], equals('HeapMap'));
expect(result['freeClassId'], isPositive);
expect(result['unitSizeBytes'], isPositive);
expect(result['pageSizeBytes'], isPositive);
expect(result['classList'], isNotNull);
expect(result['pages'].length, isPositive);
expect(result['pages'][0]['objectStart'], new isInstanceOf<String>());
expect(result['pages'][0]['objects'].length, isPositive);
expect(result['pages'][0]['objects'][0], isPositive);
},
(Isolate isolate) async {
var params = {'gc': 'mark-sweep'};
var result = await isolate.invokeRpcNoUpgrade('_getHeapMap', params);
expect(result['type'], equals('HeapMap'));
expect(result['freeClassId'], isPositive);
expect(result['unitSizeBytes'], isPositive);
expect(result['pageSizeBytes'], isPositive);
expect(result['classList'], isNotNull);
expect(result['pages'].length, isPositive);
expect(result['pages'][0]['objectStart'], new isInstanceOf<String>());
expect(result['pages'][0]['objects'].length, isPositive);
expect(result['pages'][0]['objects'][0], isPositive);
},
(Isolate isolate) async {
var params = {'gc': 'mark-compact'};
var result = await isolate.invokeRpcNoUpgrade('_getHeapMap', params);
expect(result['type'], equals('HeapMap'));
expect(result['freeClassId'], isPositive);
expect(result['unitSizeBytes'], isPositive);
expect(result['pageSizeBytes'], isPositive);
expect(result['classList'], isNotNull);
expect(result['pages'].length, isPositive);
expect(result['pages'][0]['objectStart'], new isInstanceOf<String>());
expect(result['pages'][0]['objects'].length, isPositive);
expect(result['pages'][0]['objects'][0], isPositive);
},
]; ];
main(args) async => runIsolateTests(args, tests); main(args) async => runIsolateTests(args, tests);

View file

@ -386,7 +386,7 @@ void Heap::EvacuateNewSpace(Thread* thread, GCReason reason) {
NOT_IN_PRODUCT(isolate()->class_table()->UpdatePromoted()); NOT_IN_PRODUCT(isolate()->class_table()->UpdatePromoted());
RecordAfterGC(kNew); RecordAfterGC(kNew);
PrintStats(); PrintStats();
NOT_IN_PRODUCT(PrintStatsToTimeline(&tds)); NOT_IN_PRODUCT(PrintStatsToTimeline(&tds, reason));
EndNewSpaceGC(); EndNewSpaceGC();
} }
} }
@ -404,7 +404,7 @@ void Heap::CollectNewSpaceGarbage(Thread* thread,
NOT_IN_PRODUCT(isolate()->class_table()->UpdatePromoted()); NOT_IN_PRODUCT(isolate()->class_table()->UpdatePromoted());
RecordAfterGC(kNew); RecordAfterGC(kNew);
PrintStats(); PrintStats();
NOT_IN_PRODUCT(PrintStatsToTimeline(&tds)); NOT_IN_PRODUCT(PrintStatsToTimeline(&tds, reason));
EndNewSpaceGC(); EndNewSpaceGC();
} }
if ((reason == kNewSpace) && old_space_.NeedsGarbageCollection()) { if ((reason == kNewSpace) && old_space_.NeedsGarbageCollection()) {
@ -421,10 +421,11 @@ void Heap::CollectOldSpaceGarbage(Thread* thread,
VMTagScope tagScope(thread, VMTag::kGCOldSpaceTagId); VMTagScope tagScope(thread, VMTag::kGCOldSpaceTagId);
TIMELINE_FUNCTION_GC_DURATION_BASIC(thread, "CollectOldGeneration"); TIMELINE_FUNCTION_GC_DURATION_BASIC(thread, "CollectOldGeneration");
NOT_IN_PRODUCT(UpdateClassHeapStatsBeforeGC(kOld)); NOT_IN_PRODUCT(UpdateClassHeapStatsBeforeGC(kOld));
old_space_.MarkSweep(); bool compact = (reason == kCompaction) || FLAG_use_compactor;
old_space_.CollectGarbage(compact);
RecordAfterGC(kOld); RecordAfterGC(kOld);
PrintStats(); PrintStats();
NOT_IN_PRODUCT(PrintStatsToTimeline(&tds)); NOT_IN_PRODUCT(PrintStatsToTimeline(&tds, reason));
// Some Code objects may have been collected so invalidate handler cache. // Some Code objects may have been collected so invalidate handler cache.
thread->isolate()->handler_info_cache()->Clear(); thread->isolate()->handler_info_cache()->Clear();
thread->isolate()->catch_entry_state_cache()->Clear(); thread->isolate()->catch_entry_state_cache()->Clear();
@ -636,6 +637,8 @@ const char* Heap::GCReasonToString(GCReason gc_reason) {
return "promotion"; return "promotion";
case kOldSpace: case kOldSpace:
return "old space"; return "old space";
case kCompaction:
return "compaction";
case kFull: case kFull:
return "full"; return "full";
case kIdle: case kIdle:
@ -829,38 +832,39 @@ void Heap::PrintStats() {
#endif // !defined(PRODUCT) #endif // !defined(PRODUCT)
} }
void Heap::PrintStatsToTimeline(TimelineEventScope* event) { void Heap::PrintStatsToTimeline(TimelineEventScope* event, GCReason reason) {
#if !defined(PRODUCT) #if !defined(PRODUCT)
if ((event == NULL) || !event->enabled()) { if ((event == NULL) || !event->enabled()) {
return; return;
} }
intptr_t arguments = event->GetNumArguments(); intptr_t arguments = event->GetNumArguments();
event->SetNumArguments(arguments + 12); event->SetNumArguments(arguments + 13);
event->FormatArgument(arguments + 0, "Before.New.Used (kB)", "%" Pd "", event->CopyArgument(arguments + 0, "Reason", GCReasonToString(reason));
event->FormatArgument(arguments + 1, "Before.New.Used (kB)", "%" Pd "",
RoundWordsToKB(stats_.before_.new_.used_in_words)); RoundWordsToKB(stats_.before_.new_.used_in_words));
event->FormatArgument(arguments + 1, "After.New.Used (kB)", "%" Pd "", event->FormatArgument(arguments + 2, "After.New.Used (kB)", "%" Pd "",
RoundWordsToKB(stats_.after_.new_.used_in_words)); RoundWordsToKB(stats_.after_.new_.used_in_words));
event->FormatArgument(arguments + 2, "Before.Old.Used (kB)", "%" Pd "", event->FormatArgument(arguments + 3, "Before.Old.Used (kB)", "%" Pd "",
RoundWordsToKB(stats_.before_.old_.used_in_words)); RoundWordsToKB(stats_.before_.old_.used_in_words));
event->FormatArgument(arguments + 3, "After.Old.Used (kB)", "%" Pd "", event->FormatArgument(arguments + 4, "After.Old.Used (kB)", "%" Pd "",
RoundWordsToKB(stats_.after_.old_.used_in_words)); RoundWordsToKB(stats_.after_.old_.used_in_words));
event->FormatArgument(arguments + 4, "Before.New.Capacity (kB)", "%" Pd "", event->FormatArgument(arguments + 5, "Before.New.Capacity (kB)", "%" Pd "",
RoundWordsToKB(stats_.before_.new_.capacity_in_words)); RoundWordsToKB(stats_.before_.new_.capacity_in_words));
event->FormatArgument(arguments + 5, "After.New.Capacity (kB)", "%" Pd "", event->FormatArgument(arguments + 6, "After.New.Capacity (kB)", "%" Pd "",
RoundWordsToKB(stats_.after_.new_.capacity_in_words)); RoundWordsToKB(stats_.after_.new_.capacity_in_words));
event->FormatArgument(arguments + 6, "Before.Old.Capacity (kB)", "%" Pd "", event->FormatArgument(arguments + 7, "Before.Old.Capacity (kB)", "%" Pd "",
RoundWordsToKB(stats_.before_.old_.capacity_in_words)); RoundWordsToKB(stats_.before_.old_.capacity_in_words));
event->FormatArgument(arguments + 7, "After.Old.Capacity (kB)", "%" Pd "", event->FormatArgument(arguments + 8, "After.Old.Capacity (kB)", "%" Pd "",
RoundWordsToKB(stats_.after_.old_.capacity_in_words)); RoundWordsToKB(stats_.after_.old_.capacity_in_words));
event->FormatArgument(arguments + 8, "Before.New.External (kB)", "%" Pd "", event->FormatArgument(arguments + 9, "Before.New.External (kB)", "%" Pd "",
RoundWordsToKB(stats_.before_.new_.external_in_words)); RoundWordsToKB(stats_.before_.new_.external_in_words));
event->FormatArgument(arguments + 9, "After.New.External (kB)", "%" Pd "", event->FormatArgument(arguments + 10, "After.New.External (kB)", "%" Pd "",
RoundWordsToKB(stats_.after_.new_.external_in_words)); RoundWordsToKB(stats_.after_.new_.external_in_words));
event->FormatArgument(arguments + 10, "Before.Old.External (kB)", "%" Pd "", event->FormatArgument(arguments + 11, "Before.Old.External (kB)", "%" Pd "",
RoundWordsToKB(stats_.before_.old_.external_in_words)); RoundWordsToKB(stats_.before_.old_.external_in_words));
event->FormatArgument(arguments + 11, "After.Old.External (kB)", "%" Pd "", event->FormatArgument(arguments + 12, "After.Old.External (kB)", "%" Pd "",
RoundWordsToKB(stats_.after_.old_.external_in_words)); RoundWordsToKB(stats_.after_.old_.external_in_words));
#endif // !defined(PRODUCT) #endif // !defined(PRODUCT)
} }

View file

@ -45,6 +45,7 @@ class Heap {
kNewSpace, kNewSpace,
kPromotion, kPromotion,
kOldSpace, kOldSpace,
kCompaction,
kFull, kFull,
kIdle, kIdle,
kGCAtAlloc, kGCAtAlloc,
@ -321,7 +322,7 @@ class Heap {
void RecordAfterGC(Space space); void RecordAfterGC(Space space);
void PrintStats(); void PrintStats();
void UpdateClassHeapStatsBeforeGC(Heap::Space space); void UpdateClassHeapStatsBeforeGC(Heap::Space space);
void PrintStatsToTimeline(TimelineEventScope* event); void PrintStatsToTimeline(TimelineEventScope* event, GCReason reason);
// Updates gc in progress flags. // Updates gc in progress flags.
bool BeginNewSpaceGC(Thread* thread); bool BeginNewSpaceGC(Thread* thread);

View file

@ -865,7 +865,7 @@ bool PageSpace::ShouldPerformIdleMarkSweep(int64_t deadline) {
return estimated_mark_completion <= deadline; return estimated_mark_completion <= deadline;
} }
void PageSpace::MarkSweep() { void PageSpace::CollectGarbage(bool compact) {
Thread* thread = Thread::Current(); Thread* thread = Thread::Current();
Isolate* isolate = heap_->isolate(); Isolate* isolate = heap_->isolate();
ASSERT(isolate == Isolate::Current()); ASSERT(isolate == Isolate::Current());
@ -984,7 +984,7 @@ void PageSpace::MarkSweep() {
mid3 = OS::GetCurrentMonotonicMicros(); mid3 = OS::GetCurrentMonotonicMicros();
if (FLAG_use_compactor) { if (compact) {
Compact(thread); Compact(thread);
} else if (FLAG_concurrent_sweep) { } else if (FLAG_concurrent_sweep) {
ConcurrentSweep(isolate); ConcurrentSweep(isolate);
@ -1039,7 +1039,7 @@ void PageSpace::MarkSweep() {
ml.NotifyAll(); ml.NotifyAll();
} }
if (FLAG_use_compactor) { if (compact) {
// Const object tables are hashed by address: rehash. // Const object tables are hashed by address: rehash.
SafepointOperationScope safepoint(thread); SafepointOperationScope safepoint(thread);
thread->isolate()->RehashConstants(); thread->isolate()->RehashConstants();

View file

@ -270,8 +270,8 @@ class PageSpace {
// code. // code.
bool ShouldCollectCode(); bool ShouldCollectCode();
// Collect the garbage in the page space using mark-sweep. // Collect the garbage in the page space using mark-sweep or mark-compact.
void MarkSweep(); void CollectGarbage(bool compact);
void AddRegionsToObjectSet(ObjectSet* set) const; void AddRegionsToObjectSet(ObjectSet* set) const;

View file

@ -3439,18 +3439,18 @@ static const MethodParameter* get_heap_map_params[] = {
static bool GetHeapMap(Thread* thread, JSONStream* js) { static bool GetHeapMap(Thread* thread, JSONStream* js) {
Isolate* isolate = thread->isolate(); Isolate* isolate = thread->isolate();
bool should_collect = false;
if (js->HasParam("gc")) { if (js->HasParam("gc")) {
if (js->ParamIs("gc", "full")) { if (js->ParamIs("gc", "scavenge")) {
should_collect = true; isolate->heap()->CollectGarbage(Heap::kNew);
} else if (js->ParamIs("gc", "mark-sweep")) {
isolate->heap()->CollectGarbage(Heap::kOld, Heap::kOldSpace);
} else if (js->ParamIs("gc", "mark-compact")) {
isolate->heap()->CollectGarbage(Heap::kOld, Heap::kCompaction);
} else { } else {
PrintInvalidParamError(js, "gc"); PrintInvalidParamError(js, "gc");
return true; return true;
} }
} }
if (should_collect) {
isolate->heap()->CollectAllGarbage();
}
isolate->heap()->PrintHeapMapToJSONStream(isolate, js); isolate->heap()->PrintHeapMapToJSONStream(isolate, js);
return true; return true;
} }