5f32027e1d
Change-Id: I26211d13d49688037114d76f648e500b98ec3295 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108560 Commit-Queue: Ben Konyi <bkonyi@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com> Reviewed-by: Siva Annamalai <asiva@google.com>
608 lines
20 KiB
C++
608 lines
20 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/class_table.h"
|
|
|
|
#include "platform/atomic.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/heap/heap.h"
|
|
#include "vm/object.h"
|
|
#include "vm/raw_object.h"
|
|
#include "vm/visitor.h"
|
|
|
|
namespace dart {
|
|
|
|
DEFINE_FLAG(bool, print_class_table, false, "Print initial class table.");
|
|
|
|
ClassTable::ClassTable()
|
|
: top_(kNumPredefinedCids),
|
|
capacity_(0),
|
|
table_(NULL),
|
|
old_tables_(new MallocGrowableArray<ClassAndSize*>()) {
|
|
NOT_IN_PRODUCT(class_heap_stats_table_ = NULL);
|
|
if (Dart::vm_isolate() == NULL) {
|
|
capacity_ = initial_capacity_;
|
|
table_ = reinterpret_cast<ClassAndSize*>(
|
|
calloc(capacity_, sizeof(ClassAndSize))); // NOLINT
|
|
} else {
|
|
// Duplicate the class table from the VM isolate.
|
|
ClassTable* vm_class_table = Dart::vm_isolate()->class_table();
|
|
capacity_ = vm_class_table->capacity_;
|
|
table_ = reinterpret_cast<ClassAndSize*>(
|
|
calloc(capacity_, sizeof(ClassAndSize))); // NOLINT
|
|
for (intptr_t i = kObjectCid; i < kInstanceCid; i++) {
|
|
table_[i] = vm_class_table->PairAt(i);
|
|
}
|
|
table_[kTypeArgumentsCid] = vm_class_table->PairAt(kTypeArgumentsCid);
|
|
table_[kFreeListElement] = vm_class_table->PairAt(kFreeListElement);
|
|
table_[kForwardingCorpse] = vm_class_table->PairAt(kForwardingCorpse);
|
|
table_[kDynamicCid] = vm_class_table->PairAt(kDynamicCid);
|
|
table_[kVoidCid] = vm_class_table->PairAt(kVoidCid);
|
|
}
|
|
#ifndef PRODUCT
|
|
class_heap_stats_table_ = reinterpret_cast<ClassHeapStats*>(
|
|
calloc(capacity_, sizeof(ClassHeapStats))); // NOLINT
|
|
for (intptr_t i = 0; i < capacity_; i++) {
|
|
class_heap_stats_table_[i].Initialize();
|
|
}
|
|
#endif // !PRODUCT
|
|
}
|
|
|
|
ClassTable::ClassTable(ClassTable* original)
|
|
: top_(original->top_),
|
|
capacity_(original->top_),
|
|
table_(original->table_),
|
|
old_tables_(NULL) {
|
|
NOT_IN_PRODUCT(class_heap_stats_table_ = NULL);
|
|
}
|
|
|
|
ClassTable::~ClassTable() {
|
|
if (old_tables_ != NULL) {
|
|
FreeOldTables();
|
|
delete old_tables_;
|
|
free(table_);
|
|
NOT_IN_PRODUCT(free(class_heap_stats_table_));
|
|
} else {
|
|
// This instance was a shallow copy. It doesn't own any memory.
|
|
NOT_IN_PRODUCT(ASSERT(class_heap_stats_table_ == NULL));
|
|
}
|
|
}
|
|
|
|
void ClassTable::AddOldTable(ClassAndSize* old_table) {
|
|
ASSERT(Thread::Current()->IsMutatorThread());
|
|
old_tables_->Add(old_table);
|
|
}
|
|
|
|
void ClassTable::FreeOldTables() {
|
|
while (old_tables_->length() > 0) {
|
|
free(old_tables_->RemoveLast());
|
|
}
|
|
}
|
|
|
|
void ClassTable::Register(const Class& cls) {
|
|
ASSERT(Thread::Current()->IsMutatorThread());
|
|
intptr_t index = cls.id();
|
|
if (index != kIllegalCid) {
|
|
ASSERT(index > 0);
|
|
ASSERT(index < kNumPredefinedCids);
|
|
ASSERT(table_[index].class_ == NULL);
|
|
ASSERT(index < capacity_);
|
|
table_[index] = ClassAndSize(cls.raw(), Class::instance_size(cls.raw()));
|
|
// Add the vtable for this predefined class into the static vtable registry
|
|
// if it has not been setup yet.
|
|
cpp_vtable cls_vtable = cls.handle_vtable();
|
|
cpp_vtable old_cls_vtable = AtomicOperations::CompareAndSwapWord(
|
|
&(Object::builtin_vtables_[index]), 0, cls_vtable);
|
|
if (old_cls_vtable != 0) {
|
|
ASSERT(old_cls_vtable == cls_vtable);
|
|
}
|
|
} else {
|
|
if (top_ == capacity_) {
|
|
// Grow the capacity of the class table.
|
|
// TODO(koda): Add ClassTable::Grow to share code.
|
|
|
|
#ifndef PRODUCT
|
|
// Wait for any marking tasks to complete. Allocation stats in the
|
|
// marker rely on the class table size not changing.
|
|
Thread* thread = Thread::Current();
|
|
thread->heap()->WaitForMarkerTasks(thread);
|
|
#endif
|
|
|
|
intptr_t new_capacity = capacity_ + capacity_increment_;
|
|
ClassAndSize* new_table = reinterpret_cast<ClassAndSize*>(
|
|
malloc(new_capacity * sizeof(ClassAndSize))); // NOLINT
|
|
memmove(new_table, table_, capacity_ * sizeof(ClassAndSize));
|
|
#ifndef PRODUCT
|
|
ClassHeapStats* new_stats_table = reinterpret_cast<ClassHeapStats*>(
|
|
realloc(class_heap_stats_table_,
|
|
new_capacity * sizeof(ClassHeapStats))); // NOLINT
|
|
#endif
|
|
for (intptr_t i = capacity_; i < new_capacity; i++) {
|
|
new_table[i] = ClassAndSize(NULL, 0);
|
|
NOT_IN_PRODUCT(new_stats_table[i].Initialize());
|
|
}
|
|
capacity_ = new_capacity;
|
|
old_tables_->Add(table_);
|
|
table_ = new_table; // TODO(koda): This should use atomics.
|
|
NOT_IN_PRODUCT(class_heap_stats_table_ = new_stats_table);
|
|
}
|
|
ASSERT(top_ < capacity_);
|
|
if (!Class::is_valid_id(top_)) {
|
|
FATAL1("Fatal error in ClassTable::Register: invalid index %" Pd "\n",
|
|
top_);
|
|
}
|
|
cls.set_id(top_);
|
|
table_[top_] = ClassAndSize(cls.raw());
|
|
top_++; // Increment next index.
|
|
}
|
|
}
|
|
|
|
void ClassTable::AllocateIndex(intptr_t index) {
|
|
if (index >= capacity_) {
|
|
// Grow the capacity of the class table.
|
|
// TODO(koda): Add ClassTable::Grow to share code.
|
|
|
|
#ifndef PRODUCT
|
|
// Wait for any marking tasks to complete. Allocation stats in the
|
|
// marker rely on the class table size not changing.
|
|
Thread* thread = Thread::Current();
|
|
thread->heap()->WaitForMarkerTasks(thread);
|
|
#endif
|
|
|
|
intptr_t new_capacity = index + capacity_increment_;
|
|
if (!Class::is_valid_id(index) || new_capacity < capacity_) {
|
|
FATAL1("Fatal error in ClassTable::Register: invalid index %" Pd "\n",
|
|
index);
|
|
}
|
|
ClassAndSize* new_table = reinterpret_cast<ClassAndSize*>(
|
|
malloc(new_capacity * sizeof(ClassAndSize))); // NOLINT
|
|
memmove(new_table, table_, capacity_ * sizeof(ClassAndSize));
|
|
#ifndef PRODUCT
|
|
ClassHeapStats* new_stats_table = reinterpret_cast<ClassHeapStats*>(
|
|
realloc(class_heap_stats_table_,
|
|
new_capacity * sizeof(ClassHeapStats))); // NOLINT
|
|
#endif
|
|
for (intptr_t i = capacity_; i < new_capacity; i++) {
|
|
new_table[i] = ClassAndSize(NULL);
|
|
NOT_IN_PRODUCT(new_stats_table[i].Initialize());
|
|
}
|
|
capacity_ = new_capacity;
|
|
old_tables_->Add(table_);
|
|
table_ = new_table; // TODO(koda): This should use atomics.
|
|
NOT_IN_PRODUCT(class_heap_stats_table_ = new_stats_table);
|
|
ASSERT(capacity_increment_ >= 1);
|
|
}
|
|
|
|
ASSERT(table_[index].class_ == NULL);
|
|
if (index >= top_) {
|
|
top_ = index + 1;
|
|
}
|
|
}
|
|
|
|
void ClassTable::Unregister(intptr_t index) {
|
|
table_[index] = ClassAndSize(NULL);
|
|
}
|
|
|
|
void ClassTable::Remap(intptr_t* old_to_new_cid) {
|
|
ASSERT(Thread::Current()->IsAtSafepoint());
|
|
intptr_t num_cids = NumCids();
|
|
ClassAndSize* cls_by_old_cid = new ClassAndSize[num_cids];
|
|
for (intptr_t i = 0; i < num_cids; i++) {
|
|
cls_by_old_cid[i] = table_[i];
|
|
}
|
|
for (intptr_t i = 0; i < num_cids; i++) {
|
|
table_[old_to_new_cid[i]] = cls_by_old_cid[i];
|
|
}
|
|
delete[] cls_by_old_cid;
|
|
}
|
|
|
|
void ClassTable::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
ASSERT(visitor != NULL);
|
|
visitor->set_gc_root_type("class table");
|
|
for (intptr_t i = 0; i < top_; i++) {
|
|
visitor->VisitPointer(reinterpret_cast<RawObject**>(&(table_[i].class_)));
|
|
}
|
|
visitor->clear_gc_root_type();
|
|
}
|
|
|
|
void ClassTable::CopySizesFromClassObjects() {
|
|
ASSERT(kIllegalCid == 0);
|
|
for (intptr_t i = 1; i < top_; i++) {
|
|
SetAt(i, At(i));
|
|
}
|
|
}
|
|
|
|
void ClassTable::Validate() {
|
|
Class& cls = Class::Handle();
|
|
for (intptr_t cid = kNumPredefinedCids; cid < top_; cid++) {
|
|
// Some of the class table entries maybe NULL as we create some
|
|
// top level classes but do not add them to the list of anonymous
|
|
// classes in a library if there are no top level fields or functions.
|
|
// Since there are no references to these top level classes they are
|
|
// not written into a full snapshot and will not be recreated when
|
|
// we read back the full snapshot. These class slots end up with NULL
|
|
// entries.
|
|
if (HasValidClassAt(cid)) {
|
|
cls = At(cid);
|
|
ASSERT(cls.IsClass());
|
|
ASSERT(cls.id() == cid);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClassTable::Print() {
|
|
Class& cls = Class::Handle();
|
|
String& name = String::Handle();
|
|
|
|
for (intptr_t i = 1; i < top_; i++) {
|
|
if (!HasValidClassAt(i)) {
|
|
continue;
|
|
}
|
|
cls = At(i);
|
|
if (cls.raw() != reinterpret_cast<RawClass*>(0)) {
|
|
name = cls.Name();
|
|
OS::PrintErr("%" Pd ": %s\n", i, name.ToCString());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClassTable::SetAt(intptr_t index, RawClass* raw_cls) {
|
|
ASSERT(index < capacity_);
|
|
if (raw_cls == NULL) {
|
|
table_[index] = ClassAndSize(raw_cls, 0);
|
|
} else {
|
|
table_[index] = ClassAndSize(raw_cls, Class::instance_size(raw_cls));
|
|
}
|
|
}
|
|
|
|
ClassAndSize::ClassAndSize(RawClass* clazz) : class_(clazz) {
|
|
size_ = clazz == NULL ? 0 : Class::instance_size(clazz);
|
|
}
|
|
|
|
#ifndef PRODUCT
|
|
void ClassTable::PrintToJSONObject(JSONObject* object) {
|
|
if (!FLAG_support_service) {
|
|
return;
|
|
}
|
|
Class& cls = Class::Handle();
|
|
object->AddProperty("type", "ClassList");
|
|
{
|
|
JSONArray members(object, "classes");
|
|
for (intptr_t i = 1; i < top_; i++) {
|
|
if (HasValidClassAt(i)) {
|
|
cls = At(i);
|
|
members.AddValue(cls);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClassHeapStats::Initialize() {
|
|
pre_gc.Reset();
|
|
post_gc.Reset();
|
|
recent.Reset();
|
|
accumulated.Reset();
|
|
last_reset.Reset();
|
|
promoted_count = 0;
|
|
promoted_size = 0;
|
|
state_ = 0;
|
|
USE(align_);
|
|
}
|
|
|
|
void ClassHeapStats::ResetAtNewGC() {
|
|
Verify();
|
|
pre_gc.new_count = post_gc.new_count + recent.new_count;
|
|
pre_gc.new_size = post_gc.new_size + recent.new_size;
|
|
pre_gc.new_external_size =
|
|
post_gc.new_external_size + recent.new_external_size;
|
|
pre_gc.old_external_size =
|
|
post_gc.old_external_size + recent.old_external_size;
|
|
// Accumulate allocations.
|
|
accumulated.new_count += recent.new_count - last_reset.new_count;
|
|
accumulated.new_size += recent.new_size - last_reset.new_size;
|
|
accumulated.new_external_size +=
|
|
recent.new_external_size - last_reset.new_external_size;
|
|
accumulated.old_external_size +=
|
|
recent.old_external_size - last_reset.old_external_size;
|
|
last_reset.ResetNew();
|
|
post_gc.ResetNew();
|
|
recent.ResetNew();
|
|
old_pre_new_gc_count_ = recent.old_count;
|
|
old_pre_new_gc_size_ = recent.old_size;
|
|
}
|
|
|
|
void ClassHeapStats::ResetAtOldGC() {
|
|
Verify();
|
|
pre_gc.old_count = post_gc.old_count + recent.old_count;
|
|
pre_gc.old_size = post_gc.old_size + recent.old_size;
|
|
pre_gc.old_external_size =
|
|
post_gc.old_external_size + recent.old_external_size;
|
|
pre_gc.new_external_size =
|
|
post_gc.new_external_size + recent.new_external_size;
|
|
// Accumulate allocations.
|
|
accumulated.old_count += recent.old_count - last_reset.old_count;
|
|
accumulated.old_size += recent.old_size - last_reset.old_size;
|
|
accumulated.old_external_size +=
|
|
recent.old_external_size - last_reset.old_external_size;
|
|
accumulated.new_external_size +=
|
|
recent.new_external_size - last_reset.new_external_size;
|
|
last_reset.ResetOld();
|
|
post_gc.ResetOld();
|
|
recent.ResetOld();
|
|
}
|
|
|
|
void ClassHeapStats::Verify() {
|
|
pre_gc.Verify();
|
|
post_gc.Verify();
|
|
recent.Verify();
|
|
accumulated.Verify();
|
|
last_reset.Verify();
|
|
}
|
|
|
|
void ClassHeapStats::UpdateSize(intptr_t instance_size) {
|
|
pre_gc.UpdateSize(instance_size);
|
|
post_gc.UpdateSize(instance_size);
|
|
recent.UpdateSize(instance_size);
|
|
accumulated.UpdateSize(instance_size);
|
|
last_reset.UpdateSize(instance_size);
|
|
promoted_size = promoted_count * instance_size;
|
|
old_pre_new_gc_size_ = old_pre_new_gc_count_ * instance_size;
|
|
}
|
|
|
|
void ClassHeapStats::ResetAccumulator() {
|
|
// Remember how much was allocated so we can subtract this from the result
|
|
// when printing.
|
|
last_reset.new_count = recent.new_count;
|
|
last_reset.new_size = recent.new_size;
|
|
last_reset.new_external_size = recent.new_external_size;
|
|
last_reset.old_count = recent.old_count;
|
|
last_reset.old_size = recent.old_size;
|
|
last_reset.old_external_size = recent.old_external_size;
|
|
accumulated.Reset();
|
|
}
|
|
|
|
void ClassHeapStats::UpdatePromotedAfterNewGC() {
|
|
promoted_count = recent.old_count - old_pre_new_gc_count_;
|
|
promoted_size = recent.old_size - old_pre_new_gc_size_;
|
|
}
|
|
|
|
void ClassHeapStats::PrintToJSONObject(const Class& cls,
|
|
JSONObject* obj,
|
|
bool internal) const {
|
|
if (!FLAG_support_service) {
|
|
return;
|
|
}
|
|
obj->AddProperty("type", "ClassHeapStats");
|
|
obj->AddProperty("class", cls);
|
|
int64_t accumulated_new =
|
|
accumulated.new_count + recent.new_count - last_reset.new_count;
|
|
int64_t accumulated_old =
|
|
accumulated.old_count + recent.old_count - last_reset.old_count;
|
|
int64_t accumulated_new_size =
|
|
accumulated.new_size + accumulated.new_external_size + recent.new_size +
|
|
recent.new_external_size - last_reset.new_size -
|
|
last_reset.new_external_size;
|
|
int64_t accumulated_old_size =
|
|
accumulated.old_size + accumulated.old_external_size + recent.old_size +
|
|
recent.old_external_size - last_reset.old_size -
|
|
last_reset.old_external_size;
|
|
int64_t instances_new = post_gc.new_count + recent.new_count;
|
|
int64_t instances_old = post_gc.old_count + recent.old_count;
|
|
int64_t live_after_gc_size_new = post_gc.new_size + post_gc.new_external_size;
|
|
int64_t live_after_gc_size_old = post_gc.old_size + post_gc.old_external_size;
|
|
int64_t allocated_since_gc_size_new =
|
|
recent.new_size + recent.new_external_size;
|
|
int64_t allocated_since_gc_size_old =
|
|
recent.old_size + recent.old_external_size;
|
|
int64_t bytes_current = live_after_gc_size_new + live_after_gc_size_old +
|
|
allocated_since_gc_size_new +
|
|
allocated_since_gc_size_old;
|
|
if (internal) {
|
|
{
|
|
JSONArray new_stats(obj, "_new");
|
|
new_stats.AddValue(pre_gc.new_count);
|
|
new_stats.AddValue(pre_gc.new_size + pre_gc.new_external_size);
|
|
new_stats.AddValue(post_gc.new_count);
|
|
new_stats.AddValue64(live_after_gc_size_new);
|
|
new_stats.AddValue(recent.new_count);
|
|
new_stats.AddValue64(allocated_since_gc_size_new);
|
|
new_stats.AddValue64(accumulated_new);
|
|
new_stats.AddValue64(accumulated_new_size);
|
|
}
|
|
{
|
|
JSONArray old_stats(obj, "_old");
|
|
old_stats.AddValue(pre_gc.old_count);
|
|
old_stats.AddValue(pre_gc.old_size + pre_gc.old_external_size);
|
|
old_stats.AddValue(post_gc.old_count);
|
|
old_stats.AddValue64(live_after_gc_size_old);
|
|
old_stats.AddValue(recent.old_count);
|
|
old_stats.AddValue64(allocated_since_gc_size_old);
|
|
old_stats.AddValue64(accumulated_old);
|
|
old_stats.AddValue64(accumulated_old_size);
|
|
}
|
|
obj->AddProperty("_promotedInstances", promoted_count);
|
|
obj->AddProperty("_promotedBytes", promoted_size);
|
|
}
|
|
obj->AddProperty64("instancesAccumulated", accumulated_new + accumulated_old);
|
|
obj->AddProperty64("accumulatedSize",
|
|
accumulated_new_size + accumulated_old_size);
|
|
obj->AddProperty64("instancesCurrent", instances_new + instances_old);
|
|
obj->AddProperty64("bytesCurrent", bytes_current);
|
|
}
|
|
|
|
void ClassTable::UpdateAllocatedOldGC(intptr_t cid, intptr_t size) {
|
|
ClassHeapStats* stats = PreliminaryStatsAt(cid);
|
|
ASSERT(stats != NULL);
|
|
ASSERT(size != 0);
|
|
stats->recent.AddOldGC(size);
|
|
}
|
|
|
|
void ClassTable::UpdateAllocatedExternalNew(intptr_t cid, intptr_t size) {
|
|
ClassHeapStats* stats = PreliminaryStatsAt(cid);
|
|
ASSERT(stats != NULL);
|
|
stats->recent.AddNewExternal(size);
|
|
}
|
|
|
|
void ClassTable::UpdateAllocatedExternalOld(intptr_t cid, intptr_t size) {
|
|
ClassHeapStats* stats = PreliminaryStatsAt(cid);
|
|
ASSERT(stats != NULL);
|
|
stats->recent.AddOldExternal(size);
|
|
}
|
|
|
|
bool ClassTable::ShouldUpdateSizeForClassId(intptr_t cid) {
|
|
return !RawObject::IsVariableSizeClassId(cid);
|
|
}
|
|
|
|
ClassHeapStats* ClassTable::StatsWithUpdatedSize(intptr_t cid) {
|
|
if (!HasValidClassAt(cid) || (cid == kFreeListElement) ||
|
|
(cid == kForwardingCorpse) || (cid == kSmiCid)) {
|
|
return NULL;
|
|
}
|
|
Class& cls = Class::Handle(At(cid));
|
|
if (!(cls.is_finalized() || cls.is_prefinalized())) {
|
|
// Not finalized.
|
|
return NULL;
|
|
}
|
|
ClassHeapStats* stats = PreliminaryStatsAt(cid);
|
|
if (ShouldUpdateSizeForClassId(cid)) {
|
|
stats->UpdateSize(cls.instance_size());
|
|
}
|
|
stats->Verify();
|
|
return stats;
|
|
}
|
|
|
|
void ClassTable::ResetCountersOld() {
|
|
for (intptr_t i = 0; i < top_; i++) {
|
|
class_heap_stats_table_[i].ResetAtOldGC();
|
|
}
|
|
}
|
|
|
|
void ClassTable::ResetCountersNew() {
|
|
for (intptr_t i = 0; i < top_; i++) {
|
|
class_heap_stats_table_[i].ResetAtNewGC();
|
|
}
|
|
}
|
|
|
|
void ClassTable::UpdatePromoted() {
|
|
for (intptr_t i = 0; i < top_; i++) {
|
|
class_heap_stats_table_[i].UpdatePromotedAfterNewGC();
|
|
}
|
|
}
|
|
|
|
intptr_t ClassTable::ClassOffsetFor(intptr_t cid) {
|
|
return cid * sizeof(ClassHeapStats); // NOLINT
|
|
}
|
|
|
|
intptr_t ClassTable::NewSpaceCounterOffsetFor(intptr_t cid) {
|
|
const intptr_t class_offset = ClassOffsetFor(cid);
|
|
const intptr_t count_field_offset =
|
|
ClassHeapStats::allocated_since_gc_new_space_offset();
|
|
return class_offset + count_field_offset;
|
|
}
|
|
|
|
intptr_t ClassTable::StateOffsetFor(intptr_t cid) {
|
|
return ClassOffsetFor(cid) + ClassHeapStats::state_offset();
|
|
}
|
|
|
|
intptr_t ClassTable::NewSpaceSizeOffsetFor(intptr_t cid) {
|
|
const uword class_offset = ClassOffsetFor(cid);
|
|
const uword size_field_offset =
|
|
ClassHeapStats::allocated_size_since_gc_new_space_offset();
|
|
return class_offset + size_field_offset;
|
|
}
|
|
|
|
void ClassTable::AllocationProfilePrintJSON(JSONStream* stream, bool internal) {
|
|
if (!FLAG_support_service) {
|
|
return;
|
|
}
|
|
Isolate* isolate = Isolate::Current();
|
|
ASSERT(isolate != NULL);
|
|
Heap* heap = isolate->heap();
|
|
ASSERT(heap != NULL);
|
|
JSONObject obj(stream);
|
|
obj.AddProperty("type", "AllocationProfile");
|
|
if (isolate->last_allocationprofile_accumulator_reset_timestamp() != 0) {
|
|
obj.AddPropertyF(
|
|
"dateLastAccumulatorReset", "%" Pd64 "",
|
|
isolate->last_allocationprofile_accumulator_reset_timestamp());
|
|
}
|
|
if (isolate->last_allocationprofile_gc_timestamp() != 0) {
|
|
obj.AddPropertyF("dateLastServiceGC", "%" Pd64 "",
|
|
isolate->last_allocationprofile_gc_timestamp());
|
|
}
|
|
|
|
if (internal) {
|
|
JSONObject heaps(&obj, "_heaps");
|
|
{ heap->PrintToJSONObject(Heap::kNew, &heaps); }
|
|
{ heap->PrintToJSONObject(Heap::kOld, &heaps); }
|
|
}
|
|
|
|
{
|
|
JSONObject memory(&obj, "memoryUsage");
|
|
{ heap->PrintMemoryUsageJSON(&memory); }
|
|
}
|
|
|
|
{
|
|
JSONArray arr(&obj, "members");
|
|
Class& cls = Class::Handle();
|
|
for (intptr_t i = 1; i < top_; i++) {
|
|
const ClassHeapStats* stats = StatsWithUpdatedSize(i);
|
|
if (stats != NULL) {
|
|
JSONObject obj(&arr);
|
|
cls = At(i);
|
|
stats->PrintToJSONObject(cls, &obj, internal);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClassTable::ResetAllocationAccumulators() {
|
|
for (intptr_t i = 1; i < top_; i++) {
|
|
ClassHeapStats* stats = StatsWithUpdatedSize(i);
|
|
if (stats != NULL) {
|
|
stats->ResetAccumulator();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClassTable::UpdateLiveOld(intptr_t cid, intptr_t size, intptr_t count) {
|
|
ClassHeapStats* stats = PreliminaryStatsAt(cid);
|
|
ASSERT(stats != NULL);
|
|
ASSERT(size >= 0);
|
|
ASSERT(count >= 0);
|
|
stats->post_gc.AddOld(size, count);
|
|
}
|
|
|
|
void ClassTable::UpdateLiveNew(intptr_t cid, intptr_t size) {
|
|
ClassHeapStats* stats = PreliminaryStatsAt(cid);
|
|
ASSERT(stats != NULL);
|
|
ASSERT(size >= 0);
|
|
stats->post_gc.AddNew(size);
|
|
}
|
|
|
|
void ClassTable::UpdateLiveNewGC(intptr_t cid, intptr_t size) {
|
|
ClassHeapStats* stats = PreliminaryStatsAt(cid);
|
|
ASSERT(stats != NULL);
|
|
ASSERT(size >= 0);
|
|
stats->post_gc.AddNewGC(size);
|
|
}
|
|
|
|
void ClassTable::UpdateLiveOldExternal(intptr_t cid, intptr_t size) {
|
|
ClassHeapStats* stats = PreliminaryStatsAt(cid);
|
|
ASSERT(stats != NULL);
|
|
ASSERT(size >= 0);
|
|
stats->post_gc.AddOldExternal(size);
|
|
}
|
|
|
|
void ClassTable::UpdateLiveNewExternal(intptr_t cid, intptr_t size) {
|
|
ClassHeapStats* stats = PreliminaryStatsAt(cid);
|
|
ASSERT(stats != NULL);
|
|
ASSERT(size >= 0);
|
|
stats->post_gc.AddNewExternal(size);
|
|
}
|
|
#endif // !PRODUCT
|
|
|
|
} // namespace dart
|