mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 13:08:01 +00:00
[ 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:
parent
fb1516c4ea
commit
689042093d
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -141,6 +141,9 @@ class WeakTable {
|
|||
}
|
||||
|
||||
void Forward(ObjectPointerVisitor* visitor);
|
||||
void ReportSurvivingAllocations(Dart_HeapSamplingReportCallback callback,
|
||||
void* context);
|
||||
void CleanupValues(Dart_HeapSamplingDeleteCallback cleanup);
|
||||
|
||||
void Reset();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue