mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 12:24:24 +00:00
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:
parent
10ec335464
commit
104336699d
7 changed files with 83 additions and 35 deletions
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue