mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 01:45:06 +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
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue