mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:39:49 +00:00
9bdd2ff1de
Some cleanups factored out of a larger CL (which refactors enter/exit of threads): * remove unused `#include "vm/thread_registry.h"` * remove unused/unnecessary fields from [Thread] object * rename IsMutator() -> IsDartMutator() * make tests using setjmp() drain the sticky error => to ensure there's no sticky error on isolate shutdown TEST=ci Change-Id: I53935e8bd0628ab3768627d6d5e01c3f0e3a57ad Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/296582 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Alexander Aprelev <aam@google.com>
414 lines
13 KiB
C++
414 lines
13 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 <memory>
|
|
|
|
#include "platform/atomic.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/heap/heap.h"
|
|
#include "vm/object.h"
|
|
#include "vm/object_graph.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(ClassTableAllocator* allocator)
|
|
: allocator_(allocator),
|
|
classes_(allocator),
|
|
top_level_classes_(allocator) {
|
|
if (Dart::vm_isolate() == nullptr) {
|
|
classes_.SetNumCidsAndCapacity(kNumPredefinedCids, kInitialCapacity);
|
|
} else {
|
|
// Duplicate the class table from the VM isolate.
|
|
ClassTable* vm_class_table = Dart::vm_isolate_group()->class_table();
|
|
classes_.SetNumCidsAndCapacity(kNumPredefinedCids,
|
|
vm_class_table->classes_.capacity());
|
|
|
|
const auto copy_info_for_cid = [&](intptr_t cid) {
|
|
classes_.At<kClassIndex>(cid) = vm_class_table->At(cid);
|
|
classes_.At<kSizeIndex>(cid) = vm_class_table->SizeAt(cid);
|
|
};
|
|
|
|
// The following cids don't have a corresponding class object in Dart code.
|
|
// We therefore need to initialize them eagerly.
|
|
COMPILE_ASSERT(kFirstInternalOnlyCid == kObjectCid + 1);
|
|
for (intptr_t i = kObjectCid; i <= kLastInternalOnlyCid; i++) {
|
|
copy_info_for_cid(i);
|
|
}
|
|
copy_info_for_cid(kTypeArgumentsCid);
|
|
copy_info_for_cid(kFreeListElement);
|
|
copy_info_for_cid(kForwardingCorpse);
|
|
copy_info_for_cid(kDynamicCid);
|
|
copy_info_for_cid(kVoidCid);
|
|
}
|
|
UpdateCachedAllocationTracingStateTablePointer();
|
|
}
|
|
|
|
ClassTable::~ClassTable() {
|
|
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
|
|
for (intptr_t i = 1; i < classes_.num_cids(); i++) {
|
|
const char* name = UserVisibleNameFor(i);
|
|
if (name != nullptr) {
|
|
free(const_cast<char*>(name));
|
|
}
|
|
}
|
|
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
|
|
}
|
|
|
|
void ClassTable::Register(const Class& cls) {
|
|
ASSERT(Thread::Current()->IsDartMutatorThread());
|
|
ASSERT(cls.id() == kIllegalCid || cls.id() < kNumPredefinedCids);
|
|
bool did_grow = false;
|
|
const classid_t cid =
|
|
cls.id() != kIllegalCid ? cls.id() : classes_.AddRow(&did_grow);
|
|
ASSERT(!IsTopLevelCid(cid));
|
|
|
|
const intptr_t instance_size =
|
|
cls.is_abstract() ? 0 : Class::host_instance_size(cls.ptr());
|
|
|
|
cls.set_id(cid);
|
|
classes_.At<kClassIndex>(cid) = cls.ptr();
|
|
classes_.At<kSizeIndex>(cid) = static_cast<int32_t>(instance_size);
|
|
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
|
|
classes_.At<kClassNameIndex>(cid) = nullptr;
|
|
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
|
|
|
|
if (did_grow) {
|
|
IsolateGroup::Current()->set_cached_class_table_table(
|
|
classes_.GetColumn<kClassIndex>());
|
|
UpdateCachedAllocationTracingStateTablePointer();
|
|
} else {
|
|
std::atomic_thread_fence(std::memory_order_release);
|
|
}
|
|
}
|
|
|
|
void ClassTable::RegisterTopLevel(const Class& cls) {
|
|
ASSERT(Thread::Current()->IsDartMutatorThread());
|
|
ASSERT(cls.id() == kIllegalCid);
|
|
|
|
bool did_grow = false;
|
|
const intptr_t index = top_level_classes_.AddRow(&did_grow);
|
|
cls.set_id(ClassTable::CidFromTopLevelIndex(index));
|
|
top_level_classes_.At<kClassIndex>(index) = cls.ptr();
|
|
}
|
|
|
|
void ClassTable::AllocateIndex(intptr_t index) {
|
|
bool did_grow = false;
|
|
if (IsTopLevelCid(index)) {
|
|
top_level_classes_.AllocateIndex(IndexFromTopLevelCid(index), &did_grow);
|
|
return;
|
|
}
|
|
|
|
classes_.AllocateIndex(index, &did_grow);
|
|
if (did_grow) {
|
|
IsolateGroup::Current()->set_cached_class_table_table(table());
|
|
UpdateCachedAllocationTracingStateTablePointer();
|
|
}
|
|
}
|
|
|
|
void ClassTable::UnregisterTopLevel(intptr_t cid) {
|
|
ASSERT(IsTopLevelCid(cid));
|
|
const intptr_t tlc_index = IndexFromTopLevelCid(cid);
|
|
top_level_classes_.At<kClassIndex>(tlc_index) = nullptr;
|
|
}
|
|
|
|
void ClassTable::Remap(intptr_t* old_to_new_cid) {
|
|
ASSERT(Thread::Current()->OwnsSafepoint());
|
|
classes_.Remap(old_to_new_cid);
|
|
}
|
|
|
|
void ClassTable::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
ASSERT(visitor != nullptr);
|
|
visitor->set_gc_root_type("class table");
|
|
|
|
const auto visit = [&](ClassPtr* table, intptr_t num_cids) {
|
|
if (num_cids == 0) {
|
|
return;
|
|
}
|
|
ObjectPtr* from = reinterpret_cast<ObjectPtr*>(&table[0]);
|
|
ObjectPtr* to = reinterpret_cast<ObjectPtr*>(&table[num_cids - 1]);
|
|
visitor->VisitPointers(from, to);
|
|
};
|
|
|
|
visit(classes_.GetColumn<kClassIndex>(), classes_.num_cids());
|
|
visit(top_level_classes_.GetColumn<kClassIndex>(),
|
|
top_level_classes_.num_cids());
|
|
visitor->clear_gc_root_type();
|
|
}
|
|
|
|
void ClassTable::CopySizesFromClassObjects() {
|
|
ASSERT(kIllegalCid == 0);
|
|
for (intptr_t i = 1; i < classes_.num_cids(); i++) {
|
|
UpdateClassSize(i, classes_.At<kClassIndex>(i));
|
|
}
|
|
}
|
|
|
|
void ClassTable::SetAt(intptr_t cid, ClassPtr raw_cls) {
|
|
if (IsTopLevelCid(cid)) {
|
|
top_level_classes_.At<kClassIndex>(IndexFromTopLevelCid(cid)) = raw_cls;
|
|
return;
|
|
}
|
|
|
|
// This is called by snapshot reader and class finalizer.
|
|
UpdateClassSize(cid, raw_cls);
|
|
classes_.At<kClassIndex>(cid) = raw_cls;
|
|
}
|
|
|
|
void ClassTable::UpdateClassSize(intptr_t cid, ClassPtr raw_cls) {
|
|
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
|
|
ASSERT(!IsTopLevelCid(cid)); // "top-level" classes don't get instantiated
|
|
const intptr_t size =
|
|
raw_cls == nullptr ? 0 : Class::host_instance_size(raw_cls);
|
|
classes_.At<kSizeIndex>(cid) = static_cast<int32_t>(size);
|
|
}
|
|
|
|
void ClassTable::Validate() {
|
|
Class& cls = Class::Handle();
|
|
for (intptr_t cid = kNumPredefinedCids; cid < classes_.num_cids(); cid++) {
|
|
// Some of the class table entries maybe nullptr 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 nullptr
|
|
// entries.
|
|
if (HasValidClassAt(cid)) {
|
|
cls = At(cid);
|
|
ASSERT(cls.IsClass());
|
|
#if defined(DART_PRECOMPILER)
|
|
// Precompiler can drop classes and set their id() to kIllegalCid.
|
|
// It still leaves them in the class table so dropped program
|
|
// structure could still be accessed while writing debug info.
|
|
ASSERT((cls.id() == cid) || (cls.id() == kIllegalCid));
|
|
#else
|
|
ASSERT(cls.id() == cid);
|
|
#endif // defined(DART_PRECOMPILER)
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClassTable::Print() {
|
|
Class& cls = Class::Handle();
|
|
String& name = String::Handle();
|
|
|
|
for (intptr_t i = 1; i < classes_.num_cids(); i++) {
|
|
if (!HasValidClassAt(i)) {
|
|
continue;
|
|
}
|
|
cls = At(i);
|
|
if (cls.ptr() != nullptr) {
|
|
name = cls.Name();
|
|
OS::PrintErr("%" Pd ": %s\n", i, name.ToCString());
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
void ClassTable::PrintObjectLayout(const char* filename) {
|
|
Class& cls = Class::Handle();
|
|
Array& fields = Array::Handle();
|
|
Field& field = Field::Handle();
|
|
|
|
JSONWriter js;
|
|
js.OpenArray();
|
|
for (intptr_t i = ClassId::kObjectCid; i < classes_.num_cids(); i++) {
|
|
if (!HasValidClassAt(i)) {
|
|
continue;
|
|
}
|
|
cls = At(i);
|
|
ASSERT(!cls.IsNull());
|
|
ASSERT(cls.id() != kIllegalCid);
|
|
ASSERT(cls.is_finalized()); // Precompiler already finalized all classes.
|
|
ASSERT(!cls.IsTopLevel());
|
|
js.OpenObject();
|
|
js.PrintProperty("class", cls.UserVisibleNameCString());
|
|
js.PrintProperty("size", cls.target_instance_size());
|
|
js.OpenArray("fields");
|
|
fields = cls.fields();
|
|
if (!fields.IsNull()) {
|
|
for (intptr_t i = 0, n = fields.Length(); i < n; ++i) {
|
|
field ^= fields.At(i);
|
|
js.OpenObject();
|
|
js.PrintProperty("field", field.UserVisibleNameCString());
|
|
if (field.is_static()) {
|
|
js.PrintPropertyBool("static", true);
|
|
} else {
|
|
js.PrintProperty("offset", field.TargetOffset());
|
|
}
|
|
js.CloseObject();
|
|
}
|
|
}
|
|
js.CloseArray();
|
|
js.CloseObject();
|
|
}
|
|
js.CloseArray();
|
|
|
|
auto file_open = Dart::file_open_callback();
|
|
auto file_write = Dart::file_write_callback();
|
|
auto file_close = Dart::file_close_callback();
|
|
if ((file_open == nullptr) || (file_write == nullptr) ||
|
|
(file_close == nullptr)) {
|
|
OS::PrintErr("warning: Could not access file callbacks.");
|
|
return;
|
|
}
|
|
|
|
void* file = file_open(filename, /*write=*/true);
|
|
if (file == nullptr) {
|
|
OS::PrintErr("warning: Failed to write object layout: %s\n", filename);
|
|
return;
|
|
}
|
|
|
|
char* output = nullptr;
|
|
intptr_t output_length = 0;
|
|
js.Steal(&output, &output_length);
|
|
file_write(output, output_length, file);
|
|
free(output);
|
|
file_close(file);
|
|
}
|
|
#endif // defined(DART_PRECOMPILER)
|
|
|
|
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
|
|
void ClassTable::PopulateUserVisibleNames() {
|
|
Class& cls = Class::Handle();
|
|
for (intptr_t i = 0; i < classes_.num_cids(); ++i) {
|
|
if (HasValidClassAt(i) && UserVisibleNameFor(i) == nullptr) {
|
|
cls = At(i);
|
|
cls.SetUserVisibleNameInClassTable();
|
|
}
|
|
}
|
|
}
|
|
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
|
|
|
|
#if !defined(PRODUCT)
|
|
|
|
void ClassTable::PrintToJSONObject(JSONObject* object) {
|
|
Class& cls = Class::Handle();
|
|
object->AddProperty("type", "ClassList");
|
|
{
|
|
JSONArray members(object, "classes");
|
|
for (intptr_t i = ClassId::kObjectCid; i < classes_.num_cids(); i++) {
|
|
if (HasValidClassAt(i)) {
|
|
cls = At(i);
|
|
members.AddValue(cls);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClassTable::AllocationProfilePrintJSON(JSONStream* stream, bool internal) {
|
|
Isolate* isolate = Isolate::Current();
|
|
ASSERT(isolate != nullptr);
|
|
auto isolate_group = isolate->group();
|
|
Heap* heap = isolate_group->heap();
|
|
ASSERT(heap != nullptr);
|
|
JSONObject obj(stream);
|
|
obj.AddProperty("type", "AllocationProfile");
|
|
if (isolate_group->last_allocationprofile_accumulator_reset_timestamp() !=
|
|
0) {
|
|
obj.AddPropertyF(
|
|
"dateLastAccumulatorReset", "%" Pd64 "",
|
|
isolate_group->last_allocationprofile_accumulator_reset_timestamp());
|
|
}
|
|
if (isolate_group->last_allocationprofile_gc_timestamp() != 0) {
|
|
obj.AddPropertyF("dateLastServiceGC", "%" Pd64 "",
|
|
isolate_group->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); }
|
|
}
|
|
|
|
Thread* thread = Thread::Current();
|
|
CountObjectsVisitor visitor(thread, NumCids());
|
|
{
|
|
HeapIterationScope iter(thread);
|
|
iter.IterateObjects(&visitor);
|
|
isolate->group()->VisitWeakPersistentHandles(&visitor);
|
|
}
|
|
|
|
{
|
|
JSONArray arr(&obj, "members");
|
|
Class& cls = Class::Handle();
|
|
for (intptr_t i = 3; i < classes_.num_cids(); i++) {
|
|
if (!HasValidClassAt(i)) continue;
|
|
|
|
cls = At(i);
|
|
if (cls.IsNull()) continue;
|
|
|
|
JSONObject obj(&arr);
|
|
obj.AddProperty("type", "ClassHeapStats");
|
|
obj.AddProperty("class", cls);
|
|
intptr_t count = visitor.new_count_[i] + visitor.old_count_[i];
|
|
intptr_t size = visitor.new_size_[i] + visitor.old_size_[i];
|
|
obj.AddProperty64("instancesAccumulated", count);
|
|
obj.AddProperty64("accumulatedSize", size);
|
|
obj.AddProperty64("instancesCurrent", count);
|
|
obj.AddProperty64("bytesCurrent", size);
|
|
|
|
if (internal) {
|
|
{
|
|
JSONArray new_stats(&obj, "_new");
|
|
new_stats.AddValue(visitor.new_count_[i]);
|
|
new_stats.AddValue(visitor.new_size_[i]);
|
|
new_stats.AddValue(visitor.new_external_size_[i]);
|
|
}
|
|
{
|
|
JSONArray old_stats(&obj, "_old");
|
|
old_stats.AddValue(visitor.old_count_[i]);
|
|
old_stats.AddValue(visitor.old_size_[i]);
|
|
old_stats.AddValue(visitor.old_external_size_[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // !PRODUCT
|
|
|
|
ClassTableAllocator::ClassTableAllocator()
|
|
: pending_freed_(new MallocGrowableArray<std::pair<void*, Deleter>>()) {}
|
|
|
|
ClassTableAllocator::~ClassTableAllocator() {
|
|
FreePending();
|
|
delete pending_freed_;
|
|
}
|
|
|
|
void ClassTableAllocator::Free(ClassTable* ptr) {
|
|
if (ptr != nullptr) {
|
|
pending_freed_->Add(std::make_pair(
|
|
ptr, [](void* ptr) { delete static_cast<ClassTable*>(ptr); }));
|
|
}
|
|
}
|
|
|
|
void ClassTableAllocator::Free(void* ptr) {
|
|
if (ptr != nullptr) {
|
|
pending_freed_->Add(std::make_pair(ptr, nullptr));
|
|
}
|
|
}
|
|
|
|
void ClassTableAllocator::FreePending() {
|
|
while (!pending_freed_->is_empty()) {
|
|
auto [ptr, deleter] = pending_freed_->RemoveLast();
|
|
if (deleter == nullptr) {
|
|
free(ptr);
|
|
} else {
|
|
deleter(ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace dart
|