Skip to content

Commit

Permalink
[API] Adds Dart_SetGCEventCallback for GC events in PRODUCT
Browse files Browse the repository at this point in the history
Bug: dart-lang#43106
Change-Id: Ied2201464eca387e0bc58ab54404041f7649e870
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/160063
Commit-Queue: Clement Skau <cskau@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
  • Loading branch information
Clement Skau authored and commit-bot@chromium.org committed Aug 31, 2020
1 parent d8d61ed commit 7a978c3
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 1 deletion.
56 changes: 56 additions & 0 deletions runtime/include/dart_tools_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,62 @@ DART_EXPORT Dart_Handle Dart_ServiceSendDataEvent(const char* stream_id,
const uint8_t* bytes,
intptr_t bytes_length);

/**
* Usage statistics for a space/generation at a particular moment in time.
*
* \param new_space Data for New Space.
*
* \param old_space Data for Old Space.
*/
typedef struct {
/**
* \param used_in_words Amount of memory space used, in words.
*
* \param capacity_in_words Memory space capacity, in words.
*
* \param external_in_words External memory, in words.
*/
struct {
intptr_t used_in_words;
intptr_t capacity_in_words;
intptr_t external_in_words;
} new_space, old_space;
} Dart_GCStats;

/**
* A Garbage Collection event with memory usage statistics.
*
* \param type The event type. Static lifetime.
*
* \param reason The reason for the GC event. Static lifetime.
*
* \param before Memory usage statistics measured before GC was performed.
*
* \param before Memory usage statistics measured after GC was performed.
*/
typedef struct {
const char* type;
const char* reason;
Dart_GCStats before;
Dart_GCStats after;
} Dart_GCEvent;

/**
* A callback invoked when the VM emits a GC event.
*
* \param event The GC event data. Pointer only valid for the duration of the
* callback.
*/
typedef void (*Dart_GCEventCallback)(Dart_GCEvent* event);

/**
* Sets the native GC event callback.
*
* \param callback A function pointer to an event handler callback function.
* A NULL value removes the existing listen callback function if any.
*/
DART_EXPORT void Dart_SetGCEventCallback(Dart_GCEventCallback callback);

/*
* ========
* Reload support
Expand Down
4 changes: 4 additions & 0 deletions runtime/vm/dart_api_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6379,6 +6379,10 @@ DART_EXPORT Dart_Handle Dart_ServiceSendDataEvent(const char* stream_id,
return Api::Success();
}

DART_EXPORT void Dart_SetGCEventCallback(Dart_GCEventCallback callback) {
Isolate::Current()->heap()->SetGCEventCallback(callback);
}

DART_EXPORT char* Dart_SetFileModifiedCallback(
Dart_FileModifiedCallback file_modified_callback) {
#if !defined(PRODUCT)
Expand Down
32 changes: 31 additions & 1 deletion runtime/vm/heap/heap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ Heap::Heap(IsolateGroup* isolate_group,
read_only_(false),
last_gc_was_old_space_(false),
assume_scavenge_will_fail_(false),
gc_on_nth_allocation_(kNoForcedGarbageCollection) {
gc_on_nth_allocation_(kNoForcedGarbageCollection),
gc_event_callback_(nullptr) {
UpdateGlobalMaxUsed();
for (int sel = 0; sel < kNumWeakSelectors; sel++) {
new_weak_tables_[sel] = new WeakTable();
Expand Down Expand Up @@ -1017,6 +1018,35 @@ void Heap::RecordAfterGC(GCType type) {
});
}
#endif // !PRODUCT
if (gc_event_callback_ != nullptr) {
Dart_GCEvent event;

event.type = GCTypeToString(stats_.type_);
event.reason = GCReasonToString(stats_.reason_);

event.before.new_space.capacity_in_words =
stats_.before_.new_.capacity_in_words;
event.before.new_space.external_in_words =
stats_.before_.new_.external_in_words;
event.before.new_space.used_in_words = stats_.before_.new_.used_in_words;
event.before.old_space.capacity_in_words =
stats_.before_.old_.capacity_in_words;
event.before.old_space.external_in_words =
stats_.before_.old_.external_in_words;
event.before.old_space.used_in_words = stats_.before_.old_.used_in_words;
event.after.new_space.capacity_in_words =
stats_.after_.new_.capacity_in_words;
event.after.new_space.external_in_words =
stats_.after_.new_.external_in_words;
event.after.new_space.used_in_words = stats_.after_.new_.used_in_words;
event.after.old_space.capacity_in_words =
stats_.after_.old_.capacity_in_words;
event.after.old_space.external_in_words =
stats_.after_.old_.external_in_words;
event.after.old_space.used_in_words = stats_.after_.old_.used_in_words;

(*gc_event_callback_)(&event);
}
}

void Heap::PrintStats() {
Expand Down
9 changes: 9 additions & 0 deletions runtime/vm/heap/heap.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#error "Should not include runtime"
#endif

#include "include/dart_tools_api.h"

#include "platform/assert.h"
#include "vm/allocation.h"
#include "vm/flags.h"
Expand Down Expand Up @@ -320,6 +322,10 @@ class Heap {

void MergeFrom(Heap* donor);

void SetGCEventCallback(Dart_GCEventCallback callback) {
gc_event_callback_ = callback;
}

private:
class GCStats : public ValueObject {
public:
Expand Down Expand Up @@ -416,6 +422,8 @@ class Heap {
// sensitive codepaths.
intptr_t gc_on_nth_allocation_;

Dart_GCEventCallback gc_event_callback_;

friend class Become; // VisitObjectPointers
friend class GCCompactor; // VisitObjectPointers
friend class Precompiler; // VisitObjects
Expand All @@ -429,6 +437,7 @@ class Heap {
friend class ProgramVisitor; // VisitObjectsImagePages
friend class Serializer; // VisitObjectsImagePages
friend class HeapTestHelper;
friend class MetricsTestHelper;

DISALLOW_COPY_AND_ASSIGN(Heap);
};
Expand Down
52 changes: 52 additions & 0 deletions runtime/vm/metrics_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "vm/json_stream.h"
#include "vm/metrics.h"
#include "vm/unit_test.h"
// #include "vm/heap.h"

namespace dart {

Expand Down Expand Up @@ -114,4 +115,55 @@ ISOLATE_UNIT_TEST_CASE(Metric_EmbedderAPI) {
}
}

class MetricsTestHelper {
public:
static void Scavenge(Thread* thread) {
thread->heap()->CollectNewSpaceGarbage(thread, Heap::kDebugging);
}
};

static uintptr_t event_counter;
static const char* last_gcevent_type;
static const char* last_gcevent_reason;

void MyGCEventCallback(Dart_GCEvent* e) {
event_counter++;
last_gcevent_type = e->type;
last_gcevent_reason = e->reason;
}

ISOLATE_UNIT_TEST_CASE(Metric_SetGCEventCallback) {
event_counter = 0;
last_gcevent_type = nullptr;
last_gcevent_reason = nullptr;

{
TransitionVMToNative transition(Thread::Current());

const char* kScript = "void main() {}";
Dart_Handle api_lib = TestCase::LoadTestScript(
kScript, /*resolver=*/nullptr, RESOLVED_USER_TEST_URI);
EXPECT_VALID(api_lib);
}

EXPECT_EQ(0UL, event_counter);
EXPECT_NULLPTR(last_gcevent_type);
EXPECT_NULLPTR(last_gcevent_reason);

Dart_SetGCEventCallback(&MyGCEventCallback);

MetricsTestHelper::Scavenge(Thread::Current());

EXPECT_EQ(1UL, event_counter);
EXPECT_STREQ("Scavenge", last_gcevent_type);
EXPECT_STREQ("debugging", last_gcevent_reason);

// This call emits 2 or 3 events.
Isolate::Current()->heap()->CollectAllGarbage(Heap::kLowMemory);

EXPECT_GE(event_counter, 3UL);
EXPECT_STREQ("MarkCompact", last_gcevent_type);
EXPECT_STREQ("low memory", last_gcevent_reason);
}

} // namespace dart

0 comments on commit 7a978c3

Please sign in to comment.