[ VM ] Re-work heap sampling profiler APIs to make use of WeakTable

TEST=updated dart_api_impl_test tests

Change-Id: Ibb6789286a88c96151a466617054d703cceed61e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/290160
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Ben Konyi 2023-03-24 14:00:09 +00:00 committed by Commit Queue
parent fb1516c4ea
commit 689042093d
12 changed files with 316 additions and 129 deletions

View file

@ -1228,6 +1228,17 @@ DART_EXPORT void* Dart_IsolateGroupData(Dart_Isolate isolate);
*/
DART_EXPORT Dart_Handle Dart_DebugName(void);
/**
* Returns the debugging name for the current isolate.
*
* This name is unique to each isolate and should only be used to make
* debugging messages more comprehensible.
*
* The returned string is scope allocated and is only valid until the next call
* to Dart_ExitScope.
*/
DART_EXPORT const char* Dart_DebugNameToCString(void);
/**
* Returns the ID for an isolate which is used to query the service protocol.
*
@ -1270,10 +1281,15 @@ DART_EXPORT void Dart_KillIsolate(Dart_Isolate isolate);
*/
DART_EXPORT void Dart_NotifyIdle(int64_t deadline);
typedef void (*Dart_HeapSamplingCallback)(void* isolate_group_data,
const char* cls_name,
Dart_WeakPersistentHandle obj,
uintptr_t size);
typedef void (*Dart_HeapSamplingReportCallback)(void* context,
intptr_t heap_size,
const char* cls_name,
void* data);
typedef void* (*Dart_HeapSamplingCreateCallback)(
Dart_Isolate isolate,
Dart_IsolateGroup isolate_group);
typedef void (*Dart_HeapSamplingDeleteCallback)(void* data);
/**
* Starts the heap sampling profiler for each thread in the VM.
@ -1285,29 +1301,45 @@ DART_EXPORT void Dart_EnableHeapSampling();
*/
DART_EXPORT void Dart_DisableHeapSampling();
/*
* Registers a callback that is invoked once per sampled allocation.
/* Registers callbacks are invoked once per sampled allocation upon object
* allocation and garbage collection.
*
* Important notes:
* |create_callback| can be used to associate additional data with the sampled
* allocation, such as a stack trace. This data pointer will be passed to
* |delete_callback| to allow for proper disposal when the object associated
* with the allocation sample is collected.
*
* - When invoked, |cls_name| will be a C String representing the class name
* of the allocated object. This pointer is stable and can be used as an
* identifier for all allocations of the class as it has the lifetime of its
* isolate group. The VM is responsible for freeing |cls_name| and should not
* be freed by the embedder.
* The provided callbacks must not call into the VM and should do as little
* work as possible to avoid performance penalities during object allocation and
* garbage collection.
*
* - |obj| is a weak persistent handle to the object which caused the
* allocation. The value of this handle will be set to null when the object is
* garbage collected. |obj| should only be used to determine whether the
* object has been collected as there is no guarantee that it has been fully
* initialized. This handle should eventually be freed with
* Dart_DeleteWeakPersistentHandle once the embedder no longer needs it.
*
* - The provided callback must not call into the VM and should do as little
* work as possible to avoid performance penalities.
* NOTE: It is a fatal error to set either callback to null once they have been
* initialized.
*/
DART_EXPORT void Dart_RegisterHeapSamplingCallback(
Dart_HeapSamplingCallback callback);
Dart_HeapSamplingCreateCallback create_callback,
Dart_HeapSamplingDeleteCallback delete_callback);
/*
* Reports the surviving allocation samples for all live isolate groups in the
* VM.
*
* When the callback is invoked:
* - |context| will be the context object provided when invoking
* |Dart_ReportSurvivingAllocations|. This can be safely set to null if not
* required.
* - |heap_size| will be equal to the size of the allocated object associated
* with the sample.
* - |cls_name| will be a C String representing
* the class name of the allocated object. This string is valid for the
* duration of the call to Dart_ReportSurvivingAllocations and can be
* freed by the VM at any point after the method returns.
* - |data| will be set to the data associated with the sample by
* |Dart_HeapSamplingCreateCallback|.
*/
DART_EXPORT void Dart_ReportSurvivingAllocations(
Dart_HeapSamplingReportCallback callback,
void* context);
/*
* Sets the average heap sampling rate based on a number of |bytes| for each

View file

@ -5,6 +5,7 @@
#include "include/dart_api.h"
#include "include/dart_native_api.h"
#include <cstring>
#include <memory>
#include <utility>
@ -1554,6 +1555,23 @@ DART_EXPORT Dart_Handle Dart_DebugName() {
static_cast<int64_t>(I->main_port()), I->name()));
}
DART_EXPORT const char* Dart_DebugNameToCString() {
Thread* thread = Thread::Current();
if (thread == nullptr) {
return nullptr;
}
Isolate* I = thread->isolate();
if (I == nullptr) {
return nullptr;
}
int64_t main_port = static_cast<int64_t>(I->main_port());
const char* fmt = "%s (%" Pd64 ")";
int length = snprintf(nullptr, 0, fmt, I->name(), main_port) + 1;
char* res = Api::TopScope(thread)->zone()->Alloc<char>(length);
snprintf(res, length, fmt, I->name(), main_port);
return res;
}
DART_EXPORT const char* Dart_IsolateServiceId(Dart_Isolate isolate) {
if (isolate == NULL) {
FATAL("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC);
@ -1841,9 +1859,24 @@ DART_EXPORT void Dart_DisableHeapSampling() {
}
DART_EXPORT void Dart_RegisterHeapSamplingCallback(
Dart_HeapSamplingCallback callback) {
Dart_HeapSamplingCreateCallback create_callback,
Dart_HeapSamplingDeleteCallback delete_callback) {
#if !defined(PRODUCT)
HeapProfileSampler::SetSamplingCallback(callback);
HeapProfileSampler::SetSamplingCallback(create_callback, delete_callback);
#endif
}
DART_EXPORT void Dart_ReportSurvivingAllocations(
Dart_HeapSamplingReportCallback callback,
void* context) {
#if !defined(PRODUCT)
CHECK_NO_ISOLATE(Thread::Current());
IsolateGroup::ForEach([&](IsolateGroup* group) {
Thread::EnterIsolateGroupAsHelper(group, Thread::kUnknownTask,
/*bypass_safepoint=*/false);
group->heap()->ReportSurvivingAllocations(callback, context);
Thread::ExitIsolateGroupAsHelper(/*bypass_safepoint=*/false);
});
#endif
}

View file

@ -10483,23 +10483,45 @@ TEST_CASE(DartAPI_UserTags) {
"Dart_SetCurrentUserTag expects argument 'user_tag' to be non-null");
}
void* last_isolate_group_data = nullptr;
const char* last_allocation_cls = nullptr;
intptr_t heap_samples = 0;
static void* HeapSamplingCreate(Dart_Isolate isolate,
Dart_IsolateGroup isolate_group) {
return strdup("test data");
}
void HeapSamplingCallback(void* isolate_group_data,
const char* cls_type,
Dart_WeakPersistentHandle obj,
uintptr_t size) {
last_isolate_group_data = isolate_group_data;
last_allocation_cls = cls_type;
static void HeapSamplingDelete(void* data) {
free(data);
}
static void* last_allocation_context = nullptr;
static intptr_t last_allocation_size = 0;
static const char* last_allocation_cls = nullptr;
static void* last_allocation_data = nullptr;
static intptr_t heap_samples = 0;
static const char* expected_allocation_cls = nullptr;
static bool found_allocation = false;
void HeapSamplingReport(void* context,
intptr_t heap_size,
const char* cls_name,
void* data) {
last_allocation_context = context;
last_allocation_size = heap_size;
last_allocation_cls = cls_name;
last_allocation_data = data;
if (strcmp(cls_name, expected_allocation_cls) == 0) {
found_allocation = true;
}
heap_samples++;
}
void ResetHeapSamplingState() {
void ResetHeapSamplingState(const char* expected_cls = nullptr) {
heap_samples = 0;
expected_allocation_cls = expected_cls;
found_allocation = false;
last_allocation_context = nullptr;
last_allocation_size = 0;
last_allocation_cls = nullptr;
last_isolate_group_data = nullptr;
last_allocation_data = nullptr;
}
// Threads won't pick up heap sampling profiler state changes until they
@ -10514,9 +10536,9 @@ void HandleInterrupts(Thread* thread) {
thread->HandleInterrupts();
}
void InitHeapSampling(Thread* thread) {
ResetHeapSamplingState();
Dart_RegisterHeapSamplingCallback(HeapSamplingCallback);
void InitHeapSampling(Thread* thread, const char* expected_cls) {
ResetHeapSamplingState(expected_cls);
Dart_RegisterHeapSamplingCallback(HeapSamplingCreate, HeapSamplingDelete);
Dart_EnableHeapSampling();
// Start with sampling on every byte allocated.
Dart_SetHeapSamplingPeriod(1);
@ -10525,11 +10547,10 @@ void InitHeapSampling(Thread* thread) {
TEST_CASE(DartAPI_HeapSampling_UserDefinedClass) {
DisableBackgroundCompilationScope scope;
auto isolate_group_data = Dart_CurrentIsolateGroupData();
const char* kScriptChars = R"(
class Bar {}
final list = [];
foo() {
final list = [];
for (int i = 0; i < 100000; ++i) {
list.add(Bar());
}
@ -10539,58 +10560,90 @@ TEST_CASE(DartAPI_HeapSampling_UserDefinedClass) {
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr);
EXPECT_VALID(lib);
InitHeapSampling(thread);
InitHeapSampling(thread, "Bar");
Dart_Handle result = Dart_Invoke(lib, NewString("foo"), 0, nullptr);
EXPECT_VALID(result);
// Exit the isolate before getting the profile.
Dart_Isolate isolate = Dart_CurrentIsolate();
Dart_ExitIsolate();
void* context = reinterpret_cast<void*>(42); // Fake data, not used.
Dart_ReportSurvivingAllocations(HeapSamplingReport, context);
EXPECT(heap_samples > 0);
EXPECT(heap_samples < 100000);
EXPECT(last_isolate_group_data == isolate_group_data);
EXPECT_STREQ(last_allocation_cls, "Bar");
EXPECT(last_allocation_context == context);
EXPECT(found_allocation);
EXPECT_STREQ(reinterpret_cast<char*>(last_allocation_data), "test data");
Dart_EnterIsolate(isolate);
}
TEST_CASE(DartAPI_HeapSampling_APIAllocations) {
auto isolate_group_data = Dart_CurrentIsolateGroupData();
InitHeapSampling(thread);
InitHeapSampling(thread, "List");
// Some simple allocations
USE(Dart_NewList(100));
EXPECT_VALID(Dart_NewList(100));
// Exit the isolate before getting the profile.
Dart_Isolate isolate = Dart_CurrentIsolate();
EXPECT(isolate != nullptr);
Dart_ExitIsolate();
Dart_ReportSurvivingAllocations(HeapSamplingReport, nullptr);
EXPECT(heap_samples > 0);
EXPECT_STREQ("List", last_allocation_cls);
EXPECT_EQ(last_isolate_group_data, isolate_group_data);
ResetHeapSamplingState();
ResetHeapSamplingState("String");
// Re-enter the isolate.
Dart_EnterIsolate(isolate);
const intptr_t kNumAllocations = 1000;
for (intptr_t i = 0; i < kNumAllocations; ++i) {
USE(AllocateOldString("str"));
EXPECT_VALID(AllocateOldString("str"));
}
// Exit the isolate after performing allocations.
Dart_ExitIsolate();
Dart_ReportSurvivingAllocations(HeapSamplingReport, nullptr);
EXPECT(heap_samples > 0);
EXPECT(heap_samples <= kNumAllocations);
EXPECT_STREQ("String", last_allocation_cls);
EXPECT_EQ(last_isolate_group_data, isolate_group_data);
EXPECT(found_allocation);
ResetHeapSamplingState();
ResetHeapSamplingState("String");
USE(Dart_NewStringFromCString("Foo"));
// Re-enter the isolate.
Dart_EnterIsolate(isolate);
EXPECT_VALID(Dart_NewStringFromCString("Foo"));
// Exit the isolate after performing allocations.
Dart_ExitIsolate();
Dart_ReportSurvivingAllocations(HeapSamplingReport, nullptr);
EXPECT(heap_samples > 0);
EXPECT_STREQ("String", last_allocation_cls);
EXPECT_EQ(last_isolate_group_data, isolate_group_data);
EXPECT(found_allocation);
ResetHeapSamplingState();
ResetHeapSamplingState("Uint8List");
USE(Dart_NewTypedData(Dart_TypedData_kUint8, 1000000));
// Re-enter the isolate.
Dart_EnterIsolate(isolate);
EXPECT_VALID(Dart_NewTypedData(Dart_TypedData_kUint8, 1000000));
// Exit the isolate after performing allocations.
Dart_ExitIsolate();
Dart_ReportSurvivingAllocations(HeapSamplingReport, nullptr);
EXPECT(heap_samples > 0);
EXPECT_STREQ("Uint8List", last_allocation_cls);
EXPECT_EQ(last_isolate_group_data, isolate_group_data);
EXPECT(found_allocation);
Dart_EnterIsolate(isolate);
}
TEST_CASE(DartAPI_HeapSampling_NonTrivialSamplingPeriod) {
DisableBackgroundCompilationScope scope;
auto isolate_group_data = Dart_CurrentIsolateGroupData();
InitHeapSampling(thread);
InitHeapSampling(thread, "List");
// Increase the sampling period and check that we don't sample each
// allocation. This should cause samples to be collected for approximately
@ -10601,15 +10654,24 @@ TEST_CASE(DartAPI_HeapSampling_NonTrivialSamplingPeriod) {
// Allocate via the embedding API.
const intptr_t kNumAllocations = 1000;
for (intptr_t i = 0; i < kNumAllocations; ++i) {
USE(Dart_NewList(10));
EXPECT_VALID(Dart_NewList(10));
}
// Exit the isolate before getting the profile.
Dart_Isolate isolate = Dart_CurrentIsolate();
EXPECT(isolate != nullptr);
Dart_ExitIsolate();
Dart_ReportSurvivingAllocations(HeapSamplingReport, nullptr);
EXPECT(heap_samples > 0);
EXPECT(heap_samples < kNumAllocations);
EXPECT_EQ(last_isolate_group_data, isolate_group_data);
// Re-enter the isolate.
Dart_EnterIsolate(isolate);
const char* kScriptChars = R"(
final list = [];
foo() {
final list = [];
for (int i = 0; i < 1000; ++i) {
list.add(List.filled(100, 0));
}
@ -10621,7 +10683,7 @@ TEST_CASE(DartAPI_HeapSampling_NonTrivialSamplingPeriod) {
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr);
EXPECT_VALID(lib);
ResetHeapSamplingState();
ResetHeapSamplingState("List");
Dart_EnableHeapSampling();
HandleInterrupts(thread);
@ -10629,26 +10691,16 @@ TEST_CASE(DartAPI_HeapSampling_NonTrivialSamplingPeriod) {
// Allocate via Dart code.
Dart_Handle result = Dart_Invoke(lib, NewString("foo"), 0, nullptr);
EXPECT_VALID(result);
// Exit the isolate after performing allocations.
Dart_ExitIsolate();
Dart_ReportSurvivingAllocations(HeapSamplingReport, nullptr);
EXPECT(heap_samples > 0);
EXPECT(heap_samples < kNumAllocations);
EXPECT(last_isolate_group_data == isolate_group_data);
Dart_EnterIsolate(isolate);
Dart_DisableHeapSampling();
// Sampling on every byte allocated.
Dart_SetHeapSamplingPeriod(1);
HandleInterrupts(thread);
// Ensure no more samples are collected.
ResetHeapSamplingState();
USE(Dart_NewList(10));
EXPECT_EQ(heap_samples, 0);
EXPECT_NULLPTR(last_allocation_cls);
EXPECT_NULLPTR(last_isolate_group_data);
// Clear heap sampling callback state.
Dart_RegisterHeapSamplingCallback(nullptr);
}
#endif // !PRODUCT

View file

@ -63,6 +63,15 @@ Heap::Heap(IsolateGroup* isolate_group,
}
Heap::~Heap() {
#if !defined(PRODUCT)
Dart_HeapSamplingDeleteCallback cleanup =
HeapProfileSampler::delete_callback();
if (cleanup != nullptr) {
new_weak_tables_[kHeapSamplingData]->CleanupValues(cleanup);
old_weak_tables_[kHeapSamplingData]->CleanupValues(cleanup);
}
#endif
for (int sel = 0; sel < kNumWeakSelectors; sel++) {
delete new_weak_tables_[sel];
delete old_weak_tables_[sel];

View file

@ -48,6 +48,9 @@ class Heap {
kCanonicalHashes,
kObjectIds,
kLoadingUnits,
#if !defined(PRODUCT)
kHeapSamplingData,
#endif
kNumWeakSelectors
};
@ -239,6 +242,12 @@ class Heap {
return GetWeakEntry(raw_obj, kLoadingUnits);
}
#if !defined(PRODUCT)
void SetHeapSamplingData(ObjectPtr obj, void* data) {
SetWeakEntry(obj, kHeapSamplingData, reinterpret_cast<intptr_t>(data));
}
#endif
// Used by the GC algorithms to propagate weak entries.
intptr_t GetWeakEntry(ObjectPtr raw_obj, WeakSelector sel) const;
void SetWeakEntry(ObjectPtr raw_obj, WeakSelector sel, intptr_t val);
@ -265,6 +274,16 @@ class Heap {
void ForwardWeakEntries(ObjectPtr before_object, ObjectPtr after_object);
void ForwardWeakTables(ObjectPointerVisitor* visitor);
#if !defined(PRODUCT)
void ReportSurvivingAllocations(Dart_HeapSamplingReportCallback callback,
void* context) {
new_weak_tables_[kHeapSamplingData]->ReportSurvivingAllocations(callback,
context);
old_weak_tables_[kHeapSamplingData]->ReportSurvivingAllocations(callback,
context);
}
#endif
void UpdateGlobalMaxUsed();
static bool IsAllocatableInNewSpace(intptr_t size) {

View file

@ -600,13 +600,23 @@ void GCMarker::ProcessWeakHandles(Thread* thread) {
void GCMarker::ProcessWeakTables(Thread* thread) {
TIMELINE_FUNCTION_GC_DURATION(thread, "ProcessWeakTables");
for (int sel = 0; sel < Heap::kNumWeakSelectors; sel++) {
Dart_HeapSamplingDeleteCallback cleanup = nullptr;
#if !defined(PRODUCT)
if (sel == Heap::kHeapSamplingData) {
cleanup = HeapProfileSampler::delete_callback();
}
#endif
WeakTable* table =
heap_->GetWeakTable(Heap::kOld, static_cast<Heap::WeakSelector>(sel));
intptr_t size = table->size();
for (intptr_t i = 0; i < size; i++) {
if (table->IsValidEntryAtExclusive(i)) {
// The object has been collected.
ObjectPtr raw_obj = table->ObjectAtExclusive(i);
if (raw_obj->IsHeapObject() && !raw_obj->untag()->IsMarked()) {
if (cleanup != nullptr) {
cleanup(reinterpret_cast<void*>(table->ValueAtExclusive(i)));
}
table->InvalidateAtExclusive(i);
}
}

View file

@ -35,7 +35,8 @@
namespace dart {
bool HeapProfileSampler::enabled_ = false;
Dart_HeapSamplingCallback HeapProfileSampler::callback_ = nullptr;
Dart_HeapSamplingCreateCallback HeapProfileSampler::create_callback_ = nullptr;
Dart_HeapSamplingDeleteCallback HeapProfileSampler::delete_callback_ = nullptr;
RwLock* HeapProfileSampler::lock_ = new RwLock();
intptr_t HeapProfileSampler::sampling_interval_ =
HeapProfileSampler::kDefaultSamplingInterval;
@ -78,10 +79,16 @@ void HeapProfileSampler::SetSamplingInterval(intptr_t bytes_interval) {
}
void HeapProfileSampler::SetSamplingCallback(
Dart_HeapSamplingCallback callback) {
Dart_HeapSamplingCreateCallback create_callback,
Dart_HeapSamplingDeleteCallback delete_callback) {
// Protect against the callback being changed in the middle of a sample.
WriteRwLocker locker(Thread::Current(), lock_);
callback_ = callback;
if ((create_callback_ != nullptr && create_callback == nullptr) ||
(delete_callback_ != nullptr && delete_callback == nullptr)) {
FATAL("Clearing sampling callbacks is prohibited.");
}
create_callback_ = create_callback;
delete_callback_ = delete_callback;
}
void HeapProfileSampler::ResetState() {
@ -190,19 +197,15 @@ void HeapProfileSampler::HandleNewTLAB(intptr_t old_tlab_remaining_space,
}
}
void HeapProfileSampler::InvokeCallbackForLastSample(
const char* type_name,
Dart_WeakPersistentHandle obj) {
void* HeapProfileSampler::InvokeCallbackForLastSample() {
ASSERT(enabled_);
ASSERT(create_callback_ != nullptr);
ReadRwLocker locker(thread_, lock_);
if (!enabled_) {
return;
}
if (callback_ != nullptr) {
callback_(
reinterpret_cast<void*>(thread_->isolate_group()->embedder_data()),
type_name, obj, last_sample_size_);
}
void* result = create_callback_(
reinterpret_cast<Dart_Isolate>(thread_->isolate()),
reinterpret_cast<Dart_IsolateGroup>(thread_->isolate_group()));
last_sample_size_ = kUninitialized;
return result;
}
void HeapProfileSampler::SampleNewSpaceAllocation(intptr_t allocation_size) {

View file

@ -11,7 +11,6 @@
#include "include/dart_api.h"
#include "vm/globals.h"
namespace dart {
@ -47,7 +46,13 @@ class HeapProfileSampler {
static void SetSamplingInterval(intptr_t bytes_interval);
// Updates the callback that's invoked when a sample is collected.
static void SetSamplingCallback(Dart_HeapSamplingCallback callback);
static void SetSamplingCallback(
Dart_HeapSamplingCreateCallback create_callback,
Dart_HeapSamplingDeleteCallback delete_callback);
static Dart_HeapSamplingDeleteCallback delete_callback() {
return delete_callback_;
}
void Initialize();
void Cleanup() {
@ -113,8 +118,7 @@ class HeapProfileSampler {
// allocations.
void HandleNewTLAB(intptr_t old_tlab_remaining_space, bool is_first_tlab);
void InvokeCallbackForLastSample(const char* type_name,
Dart_WeakPersistentHandle obj);
void* InvokeCallbackForLastSample();
bool HasOutstandingSample() const {
return last_sample_size_ != kUninitialized;
@ -150,7 +154,8 @@ class HeapProfileSampler {
// state from instances of HeapProfileSampler.
static RwLock* lock_;
static bool enabled_;
static Dart_HeapSamplingCallback callback_;
static Dart_HeapSamplingCreateCallback create_callback_;
static Dart_HeapSamplingDeleteCallback delete_callback_;
static intptr_t sampling_interval_;
static const intptr_t kUninitialized = -1;

View file

@ -1411,7 +1411,8 @@ void Scavenger::MournWeakTables() {
TIMELINE_FUNCTION_GC_DURATION(Thread::Current(), "MournWeakTables");
auto rehash_weak_table = [](WeakTable* table, WeakTable* replacement_new,
WeakTable* replacement_old) {
WeakTable* replacement_old,
Dart_HeapSamplingDeleteCallback cleanup) {
intptr_t size = table->size();
for (intptr_t i = 0; i < size; i++) {
if (table->IsValidEntryAtExclusive(i)) {
@ -1426,6 +1427,11 @@ void Scavenger::MournWeakTables() {
raw_obj->IsNewObject() ? replacement_new : replacement_old;
replacement->SetValueExclusive(raw_obj, table->ValueAtExclusive(i));
}
} else {
// The object has been collected.
if (cleanup != nullptr) {
cleanup(reinterpret_cast<void*>(table->ValueAtExclusive(i)));
}
}
}
};
@ -1438,7 +1444,14 @@ void Scavenger::MournWeakTables() {
// Create a new weak table for the new-space.
auto table_new = WeakTable::NewFrom(table);
rehash_weak_table(table, table_new, table_old);
Dart_HeapSamplingDeleteCallback cleanup = nullptr;
#if !defined(PRODUCT)
if (sel == Heap::kHeapSamplingData) {
cleanup = HeapProfileSampler::delete_callback();
}
#endif
rehash_weak_table(table, table_new, table_old, cleanup);
heap_->SetWeakTable(Heap::kNew, selector, table_new);
// Remove the old table as it has been replaced with the newly allocated
@ -1453,7 +1466,8 @@ void Scavenger::MournWeakTables() {
auto table = isolate->forward_table_new();
if (table != nullptr) {
auto replacement = WeakTable::NewFrom(table);
rehash_weak_table(table, replacement, isolate->forward_table_old());
rehash_weak_table(table, replacement, isolate->forward_table_old(),
nullptr);
isolate->set_forward_table_new(replacement);
}
},

View file

@ -5,6 +5,7 @@
#include "vm/heap/weak_table.h"
#include "platform/assert.h"
#include "vm/isolate.h"
#include "vm/raw_object.h"
namespace dart {
@ -139,6 +140,31 @@ void WeakTable::Forward(ObjectPointerVisitor* visitor) {
Rehash();
}
#if !defined(PRODUCT)
void WeakTable::ReportSurvivingAllocations(
Dart_HeapSamplingReportCallback callback,
void* context) {
ClassTable* table = IsolateGroup::Current()->class_table();
MutexLocker ml(&mutex_);
for (intptr_t i = 0; i < size_; i++) {
if (IsValidEntryAtExclusive(i)) {
ObjectPtr obj = static_cast<ObjectPtr>(data_[ObjectIndex(i)]);
void* data = reinterpret_cast<void*>(data_[ValueIndex(i)]);
callback(context, obj->untag()->HeapSize(),
table->UserVisibleNameFor(obj->GetClassId()), data);
}
}
}
void WeakTable::CleanupValues(Dart_HeapSamplingDeleteCallback cleanup) {
for (intptr_t i = 0; i < size_; i++) {
if (IsValidEntryAtExclusive(i)) {
cleanup(reinterpret_cast<void*>(data_[ValueIndex(i)]));
}
}
}
#endif
void WeakTable::Rehash() {
intptr_t old_size = size();
intptr_t* old_data = data_;

View file

@ -141,6 +141,9 @@ class WeakTable {
}
void Forward(ObjectPointerVisitor* visitor);
void ReportSurvivingAllocations(Dart_HeapSamplingReportCallback callback,
void* context);
void CleanupValues(Dart_HeapSamplingDeleteCallback cleanup);
void Reset();

View file

@ -2785,10 +2785,6 @@ void Object::CheckHandle() const {
#endif
}
#if !defined(PRODUCT)
static void NoopFinalizer(void* isolate_callback_data, void* peer) {}
#endif
ObjectPtr Object::Allocate(intptr_t cls_id,
intptr_t size,
Heap::Space space,
@ -2837,29 +2833,14 @@ ObjectPtr Object::Allocate(intptr_t cls_id,
#if !defined(PRODUCT)
HeapProfileSampler& heap_sampler = thread->heap_sampler();
auto class_table = thread->isolate_group()->class_table();
if (heap_sampler.HasOutstandingSample()) {
IsolateGroup* isolate_group = thread->isolate_group();
Api::Scope api_scope(thread);
const char* type_name = class_table->UserVisibleNameFor(cls_id);
if (type_name == nullptr) {
// Try the vm-isolate's class table for core types.
type_name =
Dart::vm_isolate_group()->class_table()->UserVisibleNameFor(cls_id);
}
// If type_name is still null, then we haven't finished initializing yet and
// should drop the sample.
if (type_name != nullptr) {
thread->IncrementNoCallbackScopeDepth();
Object& obj = Object::Handle(raw_obj);
auto weak_obj = FinalizablePersistentHandle::New(
isolate_group, obj, nullptr, NoopFinalizer, 0, /*auto_delete=*/false);
heap_sampler.InvokeCallbackForLastSample(
type_name, weak_obj->ApiWeakPersistentHandle());
thread->DecrementNoCallbackScopeDepth();
}
thread->IncrementNoCallbackScopeDepth();
void* data = heap_sampler.InvokeCallbackForLastSample();
heap->SetHeapSamplingData(raw_obj, data);
thread->DecrementNoCallbackScopeDepth();
}
auto class_table = thread->isolate_group()->class_table();
if (class_table->ShouldTraceAllocationFor(cls_id)) {
uint32_t hash =
HeapSnapshotWriter::GetHeapSnapshotIdentityHash(thread, raw_obj);