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 NavIsolateMenuElement(_isolate, _events, queue: _r.queue),
navMenu('heap map'),
new NavRefreshElement(label: 'GC', queue: _r.queue)
..onRefresh.listen((_) => _refresh(gc: true)),
new NavRefreshElement(label: 'Mark-Compact', queue: _r.queue)
..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)
..onRefresh.listen((_) => _refresh()),
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;
var params = {};
if (gc) {
params['gc'] = 'full';
if (gc != null) {
params['gc'] = gc;
}
return isolate
.invokeRpc('_getHeapMap', params)

View file

@ -22,6 +22,45 @@ var tests = [
expect(result['pages'][0]['objects'].length, 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);

View file

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

View file

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

View file

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

View file

@ -270,8 +270,8 @@ class PageSpace {
// code.
bool ShouldCollectCode();
// Collect the garbage in the page space using mark-sweep.
void MarkSweep();
// Collect the garbage in the page space using mark-sweep or mark-compact.
void CollectGarbage(bool compact);
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) {
Isolate* isolate = thread->isolate();
bool should_collect = false;
if (js->HasParam("gc")) {
if (js->ParamIs("gc", "full")) {
should_collect = true;
if (js->ParamIs("gc", "scavenge")) {
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 {
PrintInvalidParamError(js, "gc");
return true;
}
}
if (should_collect) {
isolate->heap()->CollectAllGarbage();
}
isolate->heap()->PrintHeapMapToJSONStream(isolate, js);
return true;
}