ART: Add GC callbacks
Add start and end of pause callbacks.
Fix setup and missing functions in test 907.
Bug: 31684633
Test: m test-art-host
Test: m test-art-host-run-test-908-gc-start-finish
Change-Id: I1d8872ef9cd6914de7fb033cd873f8d5cb48ea17
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index 18c4adf..ed16854 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -25,6 +25,8 @@
#include "base/systrace.h"
#include "base/time_utils.h"
#include "gc/accounting/heap_bitmap.h"
+#include "gc/gc_pause_listener.h"
+#include "gc/heap.h"
#include "gc/space/large_object_space.h"
#include "gc/space/space-inl.h"
#include "thread-inl.h"
@@ -156,12 +158,22 @@
GarbageCollector::ScopedPause::ScopedPause(GarbageCollector* collector)
: start_time_(NanoTime()), collector_(collector) {
- Runtime::Current()->GetThreadList()->SuspendAll(__FUNCTION__);
+ Runtime* runtime = Runtime::Current();
+ runtime->GetThreadList()->SuspendAll(__FUNCTION__);
+ GcPauseListener* pause_listener = runtime->GetHeap()->GetGcPauseListener();
+ if (pause_listener != nullptr) {
+ pause_listener->StartPause();
+ }
}
GarbageCollector::ScopedPause::~ScopedPause() {
collector_->RegisterPause(NanoTime() - start_time_);
- Runtime::Current()->GetThreadList()->ResumeAll();
+ Runtime* runtime = Runtime::Current();
+ GcPauseListener* pause_listener = runtime->GetHeap()->GetGcPauseListener();
+ if (pause_listener != nullptr) {
+ pause_listener->EndPause();
+ }
+ runtime->GetThreadList()->ResumeAll();
}
// Returns the current GC iteration and assocated info.
diff --git a/runtime/gc/gc_pause_listener.h b/runtime/gc/gc_pause_listener.h
new file mode 100644
index 0000000..da35d2a
--- /dev/null
+++ b/runtime/gc/gc_pause_listener.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_GC_PAUSE_LISTENER_H_
+#define ART_RUNTIME_GC_GC_PAUSE_LISTENER_H_
+
+namespace art {
+namespace gc {
+
+class GcPauseListener {
+ public:
+ virtual ~GcPauseListener() {}
+
+ virtual void StartPause() = 0;
+ virtual void EndPause() = 0;
+};
+
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_GC_PAUSE_LISTENER_H_
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 918b8db..3b9abd2 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -58,6 +58,7 @@
#include "gc/space/zygote_space.h"
#include "gc/task_processor.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
+#include "gc_pause_listener.h"
#include "heap-inl.h"
#include "image.h"
#include "intern_table.h"
@@ -4216,6 +4217,13 @@
}
}
+void Heap::SetGcPauseListener(GcPauseListener* l) {
+ gc_pause_listener_.StoreRelaxed(l);
+}
+
+void Heap::RemoveGcPauseListener() {
+ gc_pause_listener_.StoreRelaxed(nullptr);
+}
} // namespace gc
} // namespace art
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 6d37140..e8eb69e 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -61,6 +61,7 @@
class AllocationListener;
class AllocRecordObjectMap;
+class GcPauseListener;
class ReferenceProcessor;
class TaskProcessor;
@@ -811,6 +812,16 @@
// reasons, we assume it stays valid when we read it (so that we don't require a lock).
void RemoveAllocationListener();
+ // Install a gc pause listener.
+ void SetGcPauseListener(GcPauseListener* l);
+ // Get the currently installed gc pause listener, or null.
+ GcPauseListener* GetGcPauseListener() {
+ return gc_pause_listener_.LoadAcquire();
+ }
+ // Remove a gc pause listener. Note: the listener must not be deleted, as for performance
+ // reasons, we assume it stays valid when we read it (so that we don't require a lock).
+ void RemoveGcPauseListener();
+
private:
class ConcurrentGCTask;
class CollectorTransitionTask;
@@ -1377,6 +1388,8 @@
// An installed allocation listener.
Atomic<AllocationListener*> alloc_listener_;
+ // An installed GC Pause listener.
+ Atomic<GcPauseListener*> gc_pause_listener_;
friend class CollectorTransitionTask;
friend class collector::GarbageCollector;
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 59e01ea..12692a1 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -34,6 +34,8 @@
#include "art_jvmti.h"
#include "base/logging.h"
#include "gc/allocation_listener.h"
+#include "gc/gc_pause_listener.h"
+#include "gc/heap.h"
#include "instrumentation.h"
#include "jni_env_ext-inl.h"
#include "mirror/class.h"
@@ -131,7 +133,7 @@
explicit JvmtiAllocationListener(EventHandler* handler) : handler_(handler) {}
void ObjectAllocated(art::Thread* self, art::ObjPtr<art::mirror::Object>* obj, size_t byte_count)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
DCHECK_EQ(self, art::Thread::Current());
if (handler_->IsEventEnabledAnywhere(JVMTI_EVENT_VM_OBJECT_ALLOC)) {
@@ -185,11 +187,74 @@
}
}
+// Report GC pauses (see spec) as GARBAGE_COLLECTION_START and GARBAGE_COLLECTION_END.
+class JvmtiGcPauseListener : public art::gc::GcPauseListener {
+ public:
+ explicit JvmtiGcPauseListener(EventHandler* handler)
+ : handler_(handler),
+ start_enabled_(false),
+ finish_enabled_(false) {}
+
+ void StartPause() OVERRIDE {
+ handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_START);
+ }
+
+ void EndPause() OVERRIDE {
+ handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH);
+ }
+
+ bool IsEnabled() {
+ return start_enabled_ || finish_enabled_;
+ }
+
+ void SetStartEnabled(bool e) {
+ start_enabled_ = e;
+ }
+
+ void SetFinishEnabled(bool e) {
+ finish_enabled_ = e;
+ }
+
+ private:
+ EventHandler* handler_;
+ bool start_enabled_;
+ bool finish_enabled_;
+};
+
+static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, jvmtiEvent event, bool enable) {
+ bool old_state = listener->IsEnabled();
+
+ if (event == JVMTI_EVENT_GARBAGE_COLLECTION_START) {
+ listener->SetStartEnabled(enable);
+ } else {
+ listener->SetFinishEnabled(enable);
+ }
+
+ bool new_state = listener->IsEnabled();
+
+ if (old_state != new_state) {
+ if (new_state) {
+ art::Runtime::Current()->GetHeap()->SetGcPauseListener(listener);
+ } else {
+ art::Runtime::Current()->GetHeap()->RemoveGcPauseListener();
+ }
+ }
+}
+
// Handle special work for the given event type, if necessary.
void EventHandler::HandleEventType(jvmtiEvent event, bool enable) {
- if (event == JVMTI_EVENT_VM_OBJECT_ALLOC) {
- SetupObjectAllocationTracking(alloc_listener_.get(), enable);
- return;
+ switch (event) {
+ case JVMTI_EVENT_VM_OBJECT_ALLOC:
+ SetupObjectAllocationTracking(alloc_listener_.get(), enable);
+ return;
+
+ case JVMTI_EVENT_GARBAGE_COLLECTION_START:
+ case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+ SetupGcPauseTracking(gc_pause_listener_.get(), event, enable);
+ return;
+
+ default:
+ break;
}
}
@@ -253,6 +318,7 @@
EventHandler::EventHandler() {
alloc_listener_.reset(new JvmtiAllocationListener(this));
+ gc_pause_listener_.reset(new JvmtiGcPauseListener(this));
}
EventHandler::~EventHandler() {
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 3212b12..07d6bfd 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -28,6 +28,7 @@
struct ArtJvmTiEnv;
class JvmtiAllocationListener;
+class JvmtiGcPauseListener;
struct EventMask {
static constexpr size_t kEventsSize = JVMTI_MAX_EVENT_TYPE_VAL - JVMTI_MIN_EVENT_TYPE_VAL + 1;
@@ -103,6 +104,7 @@
EventMask global_mask;
std::unique_ptr<JvmtiAllocationListener> alloc_listener_;
+ std::unique_ptr<JvmtiGcPauseListener> gc_pause_listener_;
};
} // namespace openjdkjvmti