# HG changeset patch # User kbarrett # Date 1492115919 14400 # Node ID dacebddcdea0187173e879aef2506c3bf132648b # Parent 24999171edf9acb63f8f9a6982baa5dfa249dc4a 8169517: WhiteBox should provide concurrent GC phase control Summary: Added WhiteBox API and G1 implementation. Reviewed-by: shade, dfazunen diff -r 24999171edf9 -r dacebddcdea0 hotspot/src/share/vm/gc/g1/concurrentMarkThread.cpp --- a/hotspot/src/share/vm/gc/g1/concurrentMarkThread.cpp Thu Apr 13 11:23:37 2017 -0700 +++ b/hotspot/src/share/vm/gc/g1/concurrentMarkThread.cpp Thu Apr 13 16:38:39 2017 -0400 @@ -1,5 +1,5 @@ - /* - * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. +/* + * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,21 +32,53 @@ #include "gc/g1/g1Policy.hpp" #include "gc/g1/suspendibleThreadSet.hpp" #include "gc/g1/vm_operations_g1.hpp" +#include "gc/shared/concurrentGCPhaseManager.hpp" #include "gc/shared/gcId.hpp" #include "gc/shared/gcTrace.hpp" #include "gc/shared/gcTraceTime.inline.hpp" #include "logging/log.hpp" #include "memory/resourceArea.hpp" #include "runtime/vmThread.hpp" +#include "utilities/debug.hpp" // ======= Concurrent Mark Thread ======== +// Check order in EXPAND_CURRENT_PHASES +STATIC_ASSERT(ConcurrentGCPhaseManager::UNCONSTRAINED_PHASE < + ConcurrentGCPhaseManager::IDLE_PHASE); + +#define EXPAND_CONCURRENT_PHASES(expander) \ + expander(ANY, = ConcurrentGCPhaseManager::UNCONSTRAINED_PHASE, NULL) \ + expander(IDLE, = ConcurrentGCPhaseManager::IDLE_PHASE, NULL) \ + expander(CONCURRENT_CYCLE,, "Concurrent Cycle") \ + expander(CLEAR_CLAIMED_MARKS,, "Concurrent Clear Claimed Marks") \ + expander(SCAN_ROOT_REGIONS,, "Concurrent Scan Root Regions") \ + expander(CONCURRENT_MARK,, "Concurrent Mark") \ + expander(MARK_FROM_ROOTS,, "Concurrent Mark From Roots") \ + expander(BEFORE_REMARK,, NULL) \ + expander(REMARK,, NULL) \ + expander(CREATE_LIVE_DATA,, "Concurrent Create Live Data") \ + expander(COMPLETE_CLEANUP,, "Concurrent Complete Cleanup") \ + expander(CLEANUP_FOR_NEXT_MARK,, "Concurrent Cleanup for Next Mark") \ + /* */ + +class G1ConcurrentPhase : public AllStatic { +public: + enum { +#define CONCURRENT_PHASE_ENUM(tag, value, ignore_title) tag value, + EXPAND_CONCURRENT_PHASES(CONCURRENT_PHASE_ENUM) +#undef CONCURRENT_PHASE_ENUM + PHASE_ID_LIMIT + }; +}; + // The CM thread is created when the G1 garbage collector is used ConcurrentMarkThread::ConcurrentMarkThread(G1ConcurrentMark* cm) : ConcurrentGCThread(), _cm(cm), _state(Idle), + _phase_manager_stack(), _vtime_accum(0.0), _vtime_mark_accum(0.0) { @@ -97,8 +129,9 @@ public: G1ConcPhaseTimer(G1ConcurrentMark* cm, const char* title) : - GCTraceConcTimeImpl(title), - _cm(cm) { + GCTraceConcTimeImpl(title), + _cm(cm) + { _cm->gc_timer_cm()->register_gc_concurrent_start(title); } @@ -107,12 +140,106 @@ } }; +static const char* const concurrent_phase_names[] = { +#define CONCURRENT_PHASE_NAME(tag, ignore_value, ignore_title) XSTR(tag), + EXPAND_CONCURRENT_PHASES(CONCURRENT_PHASE_NAME) +#undef CONCURRENT_PHASE_NAME + NULL // terminator +}; +// Verify dense enum assumption. +1 for terminator. +STATIC_ASSERT(G1ConcurrentPhase::PHASE_ID_LIMIT + 1 == + ARRAY_SIZE(concurrent_phase_names)); + +// Returns the phase number for name, or a negative value if unknown. +static int lookup_concurrent_phase(const char* name) { + const char* const* names = concurrent_phase_names; + for (uint i = 0; names[i] != NULL; ++i) { + if (strcmp(name, names[i]) == 0) { + return static_cast(i); + } + } + return -1; +} + +// The phase must be valid and must have a title. +static const char* lookup_concurrent_phase_title(int phase) { + static const char* const titles[] = { +#define CONCURRENT_PHASE_TITLE(ignore_tag, ignore_value, title) title, + EXPAND_CONCURRENT_PHASES(CONCURRENT_PHASE_TITLE) +#undef CONCURRENT_PHASE_TITLE + }; + // Verify dense enum assumption. + STATIC_ASSERT(G1ConcurrentPhase::PHASE_ID_LIMIT == ARRAY_SIZE(titles)); + + assert(0 <= phase, "precondition"); + assert((uint)phase < ARRAY_SIZE(titles), "precondition"); + const char* title = titles[phase]; + assert(title != NULL, "precondition"); + return title; +} + +class G1ConcPhaseManager : public StackObj { + G1ConcurrentMark* _cm; + ConcurrentGCPhaseManager _manager; + +public: + G1ConcPhaseManager(int phase, ConcurrentMarkThread* thread) : + _cm(thread->cm()), + _manager(phase, thread->phase_manager_stack()) + { } + + ~G1ConcPhaseManager() { + // Deactivate the manager if marking aborted, to avoid blocking on + // phase exit when the phase has been requested. + if (_cm->has_aborted()) { + _manager.deactivate(); + } + } + + void set_phase(int phase, bool force) { + _manager.set_phase(phase, force); + } +}; + +// Combine phase management and timing into one convenient utility. +class G1ConcPhase : public StackObj { + G1ConcPhaseTimer _timer; + G1ConcPhaseManager _manager; + +public: + G1ConcPhase(int phase, ConcurrentMarkThread* thread) : + _timer(thread->cm(), lookup_concurrent_phase_title(phase)), + _manager(phase, thread) + { } +}; + +const char* const* ConcurrentMarkThread::concurrent_phases() const { + return concurrent_phase_names; +} + +bool ConcurrentMarkThread::request_concurrent_phase(const char* phase_name) { + int phase = lookup_concurrent_phase(phase_name); + if (phase < 0) return false; + + while (!ConcurrentGCPhaseManager::wait_for_phase(phase, + phase_manager_stack())) { + assert(phase != G1ConcurrentPhase::ANY, "Wait for ANY phase must succeed"); + if ((phase != G1ConcurrentPhase::IDLE) && !during_cycle()) { + // If idle and the goal is !idle, start a collection. + G1CollectedHeap::heap()->collect(GCCause::_wb_conc_mark); + } + } + return true; +} + void ConcurrentMarkThread::run_service() { _vtime_start = os::elapsedVTime(); G1CollectedHeap* g1h = G1CollectedHeap::heap(); G1Policy* g1_policy = g1h->g1_policy(); + G1ConcPhaseManager cpmanager(G1ConcurrentPhase::IDLE, this); + while (!should_terminate()) { // wait until started is set. sleepBeforeNextCycle(); @@ -120,6 +247,8 @@ break; } + cpmanager.set_phase(G1ConcurrentPhase::CONCURRENT_CYCLE, false /* force */); + GCIdMark gc_id_mark; cm()->concurrent_cycle_start(); @@ -133,7 +262,7 @@ double cycle_start = os::elapsedVTime(); { - G1ConcPhaseTimer t(_cm, "Concurrent Clear Claimed Marks"); + G1ConcPhase p(G1ConcurrentPhase::CLEAR_CLAIMED_MARKS, this); ClassLoaderDataGraph::clear_claimed_marks(); } @@ -146,47 +275,68 @@ // correctness issue. { - G1ConcPhaseTimer t(_cm, "Concurrent Scan Root Regions"); + G1ConcPhase p(G1ConcurrentPhase::SCAN_ROOT_REGIONS, this); _cm->scan_root_regions(); } - // It would be nice to use the GCTraceConcTime class here but + // It would be nice to use the G1ConcPhase class here but // the "end" logging is inside the loop and not at the end of - // a scope. Mimicking the same log output as GCTraceConcTime instead. - jlong mark_start = os::elapsed_counter(); - log_info(gc, marking)("Concurrent Mark (%.3fs)", TimeHelper::counter_to_seconds(mark_start)); + // a scope. Also, the timer doesn't support nesting. + // Mimicking the same log output instead. + { + G1ConcPhaseManager mark_manager(G1ConcurrentPhase::CONCURRENT_MARK, this); + jlong mark_start = os::elapsed_counter(); + const char* cm_title = + lookup_concurrent_phase_title(G1ConcurrentPhase::CONCURRENT_MARK); + log_info(gc, marking)("%s (%.3fs)", + cm_title, + TimeHelper::counter_to_seconds(mark_start)); + for (uint iter = 1; !cm()->has_aborted(); ++iter) { + // Concurrent marking. + { + G1ConcPhase p(G1ConcurrentPhase::MARK_FROM_ROOTS, this); + _cm->mark_from_roots(); + } + if (cm()->has_aborted()) break; - for (uint iter = 1; true; ++iter) { - if (!cm()->has_aborted()) { - G1ConcPhaseTimer t(_cm, "Concurrent Mark From Roots"); - _cm->mark_from_roots(); - } + // Provide a control point after mark_from_roots. + { + G1ConcPhaseManager p(G1ConcurrentPhase::BEFORE_REMARK, this); + } + if (cm()->has_aborted()) break; - double mark_end_time = os::elapsedVTime(); - jlong mark_end = os::elapsed_counter(); - _vtime_mark_accum += (mark_end_time - cycle_start); - if (!cm()->has_aborted()) { + // Delay remark pause for MMU. + double mark_end_time = os::elapsedVTime(); + jlong mark_end = os::elapsed_counter(); + _vtime_mark_accum += (mark_end_time - cycle_start); delay_to_keep_mmu(g1_policy, true /* remark */); - log_info(gc, marking)("Concurrent Mark (%.3fs, %.3fs) %.3fms", + if (cm()->has_aborted()) break; + + // Pause Remark. + log_info(gc, marking)("%s (%.3fs, %.3fs) %.3fms", + cm_title, TimeHelper::counter_to_seconds(mark_start), TimeHelper::counter_to_seconds(mark_end), TimeHelper::counter_to_millis(mark_end - mark_start)); - + mark_manager.set_phase(G1ConcurrentPhase::REMARK, false); CMCheckpointRootsFinalClosure final_cl(_cm); VM_CGC_Operation op(&final_cl, "Pause Remark"); VMThread::execute(&op); + if (cm()->has_aborted()) { + break; + } else if (!cm()->restart_for_overflow()) { + break; // Exit loop if no restart requested. + } else { + // Loop to restart for overflow. + mark_manager.set_phase(G1ConcurrentPhase::CONCURRENT_MARK, false); + log_info(gc, marking)("%s Restart for Mark Stack Overflow (iteration #%u)", + cm_title, iter); + } } - - if (!cm()->restart_for_overflow() || cm()->has_aborted()) { - break; - } - - log_info(gc, marking)("Concurrent Mark Restart due to overflow" - " (iteration #%u", iter); } if (!cm()->has_aborted()) { - G1ConcPhaseTimer t(_cm, "Concurrent Create Live Data"); + G1ConcPhase p(G1ConcurrentPhase::CREATE_LIVE_DATA, this); cm()->create_live_data(); } @@ -220,8 +370,8 @@ // place, it would wait for us to process the regions // reclaimed by cleanup. - G1ConcPhaseTimer t(_cm, "Concurrent Complete Cleanup"); // Now do the concurrent cleanup operation. + G1ConcPhase p(G1ConcurrentPhase::COMPLETE_CLEANUP, this); _cm->complete_cleanup(); // Notify anyone who's waiting that there are no more free @@ -276,7 +426,7 @@ // We may have aborted just before the remark. Do not bother clearing the // bitmap then, as it has been done during mark abort. if (!cm()->has_aborted()) { - G1ConcPhaseTimer t(_cm, "Concurrent Cleanup for Next Mark"); + G1ConcPhase p(G1ConcurrentPhase::CLEANUP_FOR_NEXT_MARK, this); _cm->cleanup_for_next_mark(); } else { assert(!G1VerifyBitmaps || _cm->nextMarkBitmapIsClear(), "Next mark bitmap must be clear"); @@ -293,6 +443,8 @@ cm()->concurrent_cycle_end(); } + + cpmanager.set_phase(G1ConcurrentPhase::IDLE, cm()->has_aborted() /* force */); } _cm->root_regions()->cancel_scan(); } diff -r 24999171edf9 -r dacebddcdea0 hotspot/src/share/vm/gc/g1/concurrentMarkThread.hpp --- a/hotspot/src/share/vm/gc/g1/concurrentMarkThread.hpp Thu Apr 13 11:23:37 2017 -0700 +++ b/hotspot/src/share/vm/gc/g1/concurrentMarkThread.hpp Thu Apr 13 16:38:39 2017 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ #ifndef SHARE_VM_GC_G1_CONCURRENTMARKTHREAD_HPP #define SHARE_VM_GC_G1_CONCURRENTMARKTHREAD_HPP +#include "gc/shared/concurrentGCPhaseManager.hpp" #include "gc/shared/concurrentGCThread.hpp" // The Concurrent Mark GC Thread triggers the parallel G1CMConcurrentMarkingTasks @@ -50,6 +51,9 @@ volatile State _state; + // WhiteBox testing support. + ConcurrentGCPhaseManager::Stack _phase_manager_stack; + void sleepBeforeNextCycle(); void delay_to_keep_mmu(G1Policy* g1_policy, bool remark); @@ -83,6 +87,14 @@ // as the CM thread might take some time to wake up before noticing // that started() is set and set in_progress(). bool during_cycle() { return !idle(); } + + // WhiteBox testing support. + const char* const* concurrent_phases() const; + bool request_concurrent_phase(const char* phase); + + ConcurrentGCPhaseManager::Stack* phase_manager_stack() { + return &_phase_manager_stack; + } }; #endif // SHARE_VM_GC_G1_CONCURRENTMARKTHREAD_HPP diff -r 24999171edf9 -r dacebddcdea0 hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp --- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp Thu Apr 13 11:23:37 2017 -0700 +++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp Thu Apr 13 16:38:39 2017 -0400 @@ -2487,6 +2487,18 @@ _verifier->verify(vo); } +bool G1CollectedHeap::supports_concurrent_phase_control() const { + return true; +} + +const char* const* G1CollectedHeap::concurrent_phases() const { + return _cmThread->concurrent_phases(); +} + +bool G1CollectedHeap::request_concurrent_phase(const char* phase) { + return _cmThread->request_concurrent_phase(phase); +} + class PrintRegionClosure: public HeapRegionClosure { outputStream* _st; public: diff -r 24999171edf9 -r dacebddcdea0 hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp --- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp Thu Apr 13 11:23:37 2017 -0700 +++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp Thu Apr 13 16:38:39 2017 -0400 @@ -1438,6 +1438,11 @@ // full GC. void verify(VerifyOption vo); + // WhiteBox testing support. + virtual bool supports_concurrent_phase_control() const; + virtual const char* const* concurrent_phases() const; + virtual bool request_concurrent_phase(const char* phase); + // The methods below are here for convenience and dispatch the // appropriate method depending on value of the given VerifyOption // parameter. The values for that parameter, and their meanings, diff -r 24999171edf9 -r dacebddcdea0 hotspot/src/share/vm/gc/shared/collectedHeap.cpp --- a/hotspot/src/share/vm/gc/shared/collectedHeap.cpp Thu Apr 13 11:23:37 2017 -0700 +++ b/hotspot/src/share/vm/gc/shared/collectedHeap.cpp Thu Apr 13 16:38:39 2017 -0400 @@ -158,6 +158,22 @@ trace_heap(GCWhen::AfterGC, gc_tracer); } +// WhiteBox API support for concurrent collectors. These are the +// default implementations, for collectors which don't support this +// feature. +bool CollectedHeap::supports_concurrent_phase_control() const { + return false; +} + +const char* const* CollectedHeap::concurrent_phases() const { + static const char* const result[] = { NULL }; + return result; +} + +bool CollectedHeap::request_concurrent_phase(const char* phase) { + return false; +} + // Memory state functions. diff -r 24999171edf9 -r dacebddcdea0 hotspot/src/share/vm/gc/shared/collectedHeap.hpp --- a/hotspot/src/share/vm/gc/shared/collectedHeap.hpp Thu Apr 13 11:23:37 2017 -0700 +++ b/hotspot/src/share/vm/gc/shared/collectedHeap.hpp Thu Apr 13 16:38:39 2017 -0400 @@ -579,6 +579,31 @@ // Heap verification virtual void verify(VerifyOption option) = 0; + // Return true if concurrent phase control (via + // request_concurrent_phase_control) is supported by this collector. + // The default implementation returns false. + virtual bool supports_concurrent_phase_control() const; + + // Return a NULL terminated array of concurrent phase names provided + // by this collector. Supports Whitebox testing. These are the + // names recognized by request_concurrent_phase(). The default + // implementation returns an array of one NULL element. + virtual const char* const* concurrent_phases() const; + + // Request the collector enter the indicated concurrent phase, and + // wait until it does so. Supports WhiteBox testing. Only one + // request may be active at a time. Phases are designated by name; + // the set of names and their meaning is GC-specific. Once the + // requested phase has been reached, the collector will attempt to + // avoid transitioning to a new phase until a new request is made. + // [Note: A collector might not be able to remain in a given phase. + // For example, a full collection might cancel an in-progress + // concurrent collection.] + // + // Returns true when the phase is reached. Returns false for an + // unknown phase. The default implementation returns false. + virtual bool request_concurrent_phase(const char* phase); + // Non product verification and debugging. #ifndef PRODUCT // Support for PromotionFailureALot. Return true if it's time to cause a diff -r 24999171edf9 -r dacebddcdea0 hotspot/src/share/vm/gc/shared/concurrentGCPhaseManager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/share/vm/gc/shared/concurrentGCPhaseManager.cpp Thu Apr 13 16:38:39 2017 -0400 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "gc/shared/concurrentGCPhaseManager.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/thread.hpp" + +#define assert_ConcurrentGC_thread() \ + assert(Thread::current()->is_ConcurrentGC_thread(), "precondition") + +#define assert_not_enter_unconstrained(phase) \ + assert((phase) != UNCONSTRAINED_PHASE, "Cannot enter \"unconstrained\" phase") + +#define assert_manager_is_tos(manager, stack, kind) \ + assert((manager) == (stack)->_top, kind " manager is not top of stack") + +ConcurrentGCPhaseManager::Stack::Stack() : + _requested_phase(UNCONSTRAINED_PHASE), + _top(NULL) +{ } + +ConcurrentGCPhaseManager::ConcurrentGCPhaseManager(int phase, Stack* stack) : + _phase(phase), + _active(true), + _prev(NULL), + _stack(stack) +{ + assert_ConcurrentGC_thread(); + assert_not_enter_unconstrained(phase); + assert(stack != NULL, "precondition"); + MonitorLockerEx ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag); + if (stack->_top != NULL) { + assert(stack->_top->_active, "precondition"); + _prev = stack->_top; + } + stack->_top = this; + ml.notify_all(); +} + +ConcurrentGCPhaseManager::~ConcurrentGCPhaseManager() { + assert_ConcurrentGC_thread(); + MonitorLockerEx ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag); + assert_manager_is_tos(this, _stack, "This"); + wait_when_requested_impl(); + _stack->_top = _prev; + ml.notify_all(); +} + +bool ConcurrentGCPhaseManager::is_requested() const { + assert_ConcurrentGC_thread(); + MonitorLockerEx ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag); + assert_manager_is_tos(this, _stack, "This"); + return _active && (_stack->_requested_phase == _phase); +} + +bool ConcurrentGCPhaseManager::wait_when_requested_impl() const { + assert_ConcurrentGC_thread(); + assert_lock_strong(CGCPhaseManager_lock); + bool waited = false; + while (_active && (_stack->_requested_phase == _phase)) { + waited = true; + CGCPhaseManager_lock->wait(Mutex::_no_safepoint_check_flag); + } + return waited; +} + +bool ConcurrentGCPhaseManager::wait_when_requested() const { + assert_ConcurrentGC_thread(); + MonitorLockerEx ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag); + assert_manager_is_tos(this, _stack, "This"); + return wait_when_requested_impl(); +} + +void ConcurrentGCPhaseManager::set_phase(int phase, bool force) { + assert_ConcurrentGC_thread(); + assert_not_enter_unconstrained(phase); + MonitorLockerEx ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag); + assert_manager_is_tos(this, _stack, "This"); + if (!force) wait_when_requested_impl(); + _phase = phase; + ml.notify_all(); +} + +void ConcurrentGCPhaseManager::deactivate() { + assert_ConcurrentGC_thread(); + MonitorLockerEx ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag); + assert_manager_is_tos(this, _stack, "This"); + _active = false; + ml.notify_all(); +} + +bool ConcurrentGCPhaseManager::wait_for_phase(int phase, Stack* stack) { + assert(Thread::current()->is_Java_thread(), "precondition"); + assert(stack != NULL, "precondition"); + MonitorLockerEx ml(CGCPhaseManager_lock); + // Update request and notify service of change. + if (stack->_requested_phase != phase) { + stack->_requested_phase = phase; + ml.notify_all(); + } + + if (phase == UNCONSTRAINED_PHASE) { + return true; + } + + // Wait until phase or IDLE is active. + while (true) { + bool idle = false; + for (ConcurrentGCPhaseManager* manager = stack->_top; + manager != NULL; + manager = manager->_prev) { + if (manager->_phase == phase) { + return true; // phase is active. + } else if (manager->_phase == IDLE_PHASE) { + idle = true; // Note idle active, continue search for phase. + } + } + if (idle) { + return false; // idle is active and phase is not. + } else { + ml.wait(); // Wait for phase change. + } + } +} diff -r 24999171edf9 -r dacebddcdea0 hotspot/src/share/vm/gc/shared/concurrentGCPhaseManager.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/share/vm/gc/shared/concurrentGCPhaseManager.hpp Thu Apr 13 16:38:39 2017 -0400 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_GC_CONCURRENTGCPHASEMANAGER_HPP +#define SHARE_VM_GC_CONCURRENTGCPHASEMANAGER_HPP + +#include "memory/allocation.hpp" + +// Manage concurrent phase information, to support WhiteBox testing. +// Managers are stack allocated. Managers may be nested, to support +// nested subphases. +class ConcurrentGCPhaseManager : public StackObj { +public: + + // Special phase ids used by all GC's that use this facility. + static const int UNCONSTRAINED_PHASE = 0; // Unconstrained or no request. + static const int IDLE_PHASE = 1; // Concurrent processing is idle. + + // Stack of phase managers. + class Stack VALUE_OBJ_CLASS_SPEC { + friend class ConcurrentGCPhaseManager; + + public: + // Create an empty stack of phase managers. + Stack(); + + private: + int _requested_phase; + ConcurrentGCPhaseManager* _top; + + // Non-copyable - never defined. + Stack(const Stack&); + Stack& operator=(const Stack&); + }; + + // Construct and push a new manager on the stack, activating phase. + // Notifies callers in wait_for_phase of the phase change. + // + // Preconditions: + // - Calling thread must be a ConcurrentGC thread + // - phase != UNCONSTRAINED_PHASE + // - stack != NULL + // - other managers on stack must all be active. + ConcurrentGCPhaseManager(int phase, Stack* stack); + + // Pop this manager off the stack, deactivating phase. Before + // changing phases, if is_requested() is true, wait until the + // request is changed. After changing phases, notifies callers of + // wait_for_phase of the phase change. + // + // Preconditions: + // - Calling thread must be a ConcurrentGC thread + // - this must be the current top of the manager stack + ~ConcurrentGCPhaseManager(); + + // Returns true if this phase is active and is currently requested. + // + // Preconditions: + // - Calling thread must be a ConcurrentGC thread + // - this must be the current top of manager stack + bool is_requested() const; + + // Wait until is_requested() is false. Returns true if waited. + // + // Preconditions: + // - Calling thread must be a ConcurrentGC thread + // - this must be the current top of manager stack + bool wait_when_requested() const; + + // Directly step from one phase to another, without needing to pop a + // manager from the stack and allocate a new one. Before changing + // phases, if is_requested() is true and force is false, wait until + // the request is changed. After changing phases, notifies callers + // of wait_for_phase of the phase change. + // + // Preconditions: + // - Calling thread must be a ConcurrentGC thread + // - phase != UNCONSTRAINED_PHASE + // - this must be the current top of manager stack + void set_phase(int phase, bool force); + + // Deactivate the manager. An inactive manager no longer blocks + // transitions out of the associated phase when that phase has been + // requested. + // + // Preconditions: + // - Calling thread must be a ConcurrentGC thread + // - this must be the current top of manager stack + void deactivate(); + + // Used to implement CollectorPolicy::request_concurrent_phase(). + // Updates request to the new phase, and notifies threads blocked on + // the old request of the change. Returns true if the phase is + // UNCONSTRAINED_PHASE. Otherwise, waits until an active phase is + // the requested phase (returning true) or IDLE_PHASE (returning + // false if not also the requested phase). + // + // Preconditions: + // - Calling thread must be a Java thread + // - stack must be non-NULL + static bool wait_for_phase(int phase, Stack* stack); + +private: + int _phase; + bool _active; + ConcurrentGCPhaseManager* _prev; + Stack* _stack; + + // Non-copyable - never defined. + ConcurrentGCPhaseManager(const ConcurrentGCPhaseManager&); + ConcurrentGCPhaseManager& operator=(const ConcurrentGCPhaseManager&); + + bool wait_when_requested_impl() const; +}; + +#endif // include guard diff -r 24999171edf9 -r dacebddcdea0 hotspot/src/share/vm/prims/whitebox.cpp --- a/hotspot/src/share/vm/prims/whitebox.cpp Thu Apr 13 11:23:37 2017 -0700 +++ b/hotspot/src/share/vm/prims/whitebox.cpp Thu Apr 13 16:38:39 2017 -0400 @@ -357,6 +357,43 @@ return (jlong)alignment; WB_END +WB_ENTRY(jboolean, WB_SupportsConcurrentGCPhaseControl(JNIEnv* env, jobject o)) + return Universe::heap()->supports_concurrent_phase_control(); +WB_END + +WB_ENTRY(jobjectArray, WB_GetConcurrentGCPhases(JNIEnv* env, jobject o)) + const char* const* phases = Universe::heap()->concurrent_phases(); + jint nphases = 0; + for ( ; phases[nphases] != NULL; ++nphases) ; + + ResourceMark rm(thread); + ThreadToNativeFromVM ttn(thread); + jclass clazz = env->FindClass(vmSymbols::java_lang_Object()->as_C_string()); + CHECK_JNI_EXCEPTION_(env, NULL); + + jobjectArray result = env->NewObjectArray(nphases, clazz, NULL); + CHECK_JNI_EXCEPTION_(env, NULL); + + // If push fails, return with pending exception. + if (env->PushLocalFrame(nphases) < 0) return NULL; + for (jint i = 0; i < nphases; ++i) { + jstring phase = env->NewStringUTF(phases[i]); + CHECK_JNI_EXCEPTION_(env, NULL); + env->SetObjectArrayElement(result, i, phase); + CHECK_JNI_EXCEPTION_(env, NULL); + } + env->PopLocalFrame(NULL); + + return result; +WB_END + +WB_ENTRY(jboolean, WB_RequestConcurrentGCPhase(JNIEnv* env, jobject o, jstring name)) + Handle h_name(THREAD, JNIHandles::resolve(name)); + ResourceMark rm; + const char* c_name = java_lang_String::as_utf8_string(h_name()); + return Universe::heap()->request_concurrent_phase(c_name); +WB_END + #if INCLUDE_ALL_GCS WB_ENTRY(jboolean, WB_G1IsHumongous(JNIEnv* env, jobject o, jobject obj)) if (UseG1GC) { @@ -1969,6 +2006,11 @@ {CC"currentGC", CC"()I", (void*)&WB_CurrentGC}, {CC"allSupportedGC", CC"()I", (void*)&WB_AllSupportedGC}, {CC"gcSelectedByErgo", CC"()Z", (void*)&WB_GCSelectedByErgo}, + {CC"supportsConcurrentGCPhaseControl", CC"()Z", (void*)&WB_SupportsConcurrentGCPhaseControl}, + {CC"getConcurrentGCPhases", CC"()[Ljava/lang/String;", + (void*)&WB_GetConcurrentGCPhases}, + {CC"requestConcurrentGCPhase0", CC"(Ljava/lang/String;)Z", + (void*)&WB_RequestConcurrentGCPhase}, }; #undef CC diff -r 24999171edf9 -r dacebddcdea0 hotspot/src/share/vm/runtime/mutexLocker.cpp --- a/hotspot/src/share/vm/runtime/mutexLocker.cpp Thu Apr 13 11:23:37 2017 -0700 +++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp Thu Apr 13 16:38:39 2017 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -81,6 +81,7 @@ Mutex* MarkStackChunkList_lock = NULL; Mutex* ParGCRareEvent_lock = NULL; Mutex* DerivedPointerTableGC_lock = NULL; +Monitor* CGCPhaseManager_lock = NULL; Mutex* Compile_lock = NULL; Monitor* MethodCompileQueue_lock = NULL; Monitor* CompileThread_lock = NULL; @@ -203,6 +204,9 @@ } def(ParGCRareEvent_lock , PaddedMutex , leaf , true, Monitor::_safepoint_check_sometimes); def(DerivedPointerTableGC_lock , PaddedMutex , leaf, true, Monitor::_safepoint_check_never); +#ifdef INCLUDE_ALL_GCS + def(CGCPhaseManager_lock , PaddedMonitor, leaf, false, Monitor::_safepoint_check_sometimes); +#endif def(CodeCache_lock , PaddedMutex , special, true, Monitor::_safepoint_check_never); def(RawMonitor_lock , PaddedMutex , special, true, Monitor::_safepoint_check_never); def(OopMapCacheAlloc_lock , PaddedMutex , leaf, true, Monitor::_safepoint_check_always); // used for oop_map_cache allocation. diff -r 24999171edf9 -r dacebddcdea0 hotspot/src/share/vm/runtime/mutexLocker.hpp --- a/hotspot/src/share/vm/runtime/mutexLocker.hpp Thu Apr 13 11:23:37 2017 -0700 +++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp Thu Apr 13 16:38:39 2017 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,6 +57,7 @@ extern Mutex* TouchedMethodLog_lock; // a lock on allocation of LogExecutedMethods info extern Mutex* RetData_lock; // a lock on installation of RetData inside method data extern Mutex* DerivedPointerTableGC_lock; // a lock to protect the derived pointer table +extern Monitor* CGCPhaseManager_lock; // a lock to protect a concurrent GC's phase management extern Monitor* VMOperationQueue_lock; // a lock on queue of vm_operations waiting to execute extern Monitor* VMOperationRequest_lock; // a lock on Threads waiting for a vm_operation to terminate extern Monitor* Safepoint_lock; // a lock used by the safepoint abstraction diff -r 24999171edf9 -r dacebddcdea0 hotspot/test/gc/concurrent_phase_control/CheckControl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/concurrent_phase_control/CheckControl.java Thu Apr 13 16:38:39 2017 -0400 @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package gc.concurrent_phase_control; + +/* + * Utility class that uses the WhiteBox concurrent GC phase control to + * step through a provided sequence of phases, and verify that the + * phases were actually reached as expected. + * + * To use: + * + * (1) The main test class has a main function which calls this helper + * class's check() function with appropriate arguments for the + * collector being tested. + * + * (2) The test program must provide access to WhiteBox, as it is used + * by this support class. + * + * (4) The main test class should be invoked as a driver. This + * helper class's check() function will run its Executor class in a + * subprocess, in order to capture its output for analysis. + */ + +import sun.hotspot.WhiteBox; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public final class CheckControl { + // gcName: The name of the GC, logged as "Using " near the + // beginning of the log output. + // + // gcOptions: Command line options for invoking the desired + // collector and logging options to produce output that can be + // matched against the regex patterns in the gcPhaseInfo pairs. + // + // gcPhaseInfo: An array of pairs of strings. Each pair is a + // phase name and a regex pattern for recognizing the associated + // log message. The regex pattern can be null if no log message + // is associated with the named phase. The test will iterate + // through the array, requesting each phase in turn. + public static void check(String gcName, + String[] gcOptions, + String[][] gcPhaseInfo) throws Exception { + String[] stepPhases = new String[gcPhaseInfo.length]; + for (int i = 0; i < gcPhaseInfo.length; ++i) { + stepPhases[i] = gcPhaseInfo[i][0]; + } + String messages = executeTest(gcName, gcOptions, stepPhases); + checkPhaseControl(messages, gcPhaseInfo); + } + + private static void fail(String message) throws Exception { + throw new RuntimeException(message); + } + + private static final String requestPrefix = "Requesting concurrent phase: "; + private static final String reachedPrefix = "Reached concurrent phase: "; + + private static String executeTest(String gcName, + String[] gcOptions, + String[] gcStepPhases) throws Exception { + System.out.println("\n---------- Testing ---------"); + + final String[] wb_arguments = { + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI" + }; + + List arglist = new ArrayList(); + Collections.addAll(arglist, wb_arguments); + Collections.addAll(arglist, gcOptions); + arglist.add(Executor.class.getName()); + Collections.addAll(arglist, gcStepPhases); + String[] arguments = arglist.toArray(new String[arglist.size()]); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(arguments); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + String messages = output.getStdout(); + System.out.println(messages); + + output.shouldHaveExitValue(0); + output.shouldContain("Using " + gcName); + + return messages; + } + + private static void checkPhaseControl(String messages, + String[][] gcPhaseInfo) + throws Exception + { + // Iterate through the phase sequence for the test, verifying + // output contains appropriate sequences of request message, + // log message for phase, and request reached message. Note + // that a log message for a phase may occur later than the + // associated request reached message, or even the following + // request message. + + Pattern nextReqP = Pattern.compile(requestPrefix); + Matcher nextReqM = nextReqP.matcher(messages); + + Pattern nextReachP = Pattern.compile(reachedPrefix); + Matcher nextReachM = nextReachP.matcher(messages); + + String pendingPhaseMessage = null; + int pendingPhaseMessagePosition = -1; + + int position = 0; + for (String[] phase: gcPhaseInfo) { + String phaseName = phase[0]; + String phaseMsg = phase[1]; + + System.out.println("Checking phase " + phaseName); + + // Update the "next" matchers to refer to the next + // corresponding pair of request and reached messages. + if (!nextReqM.find()) { + fail("Didn't find next phase request"); + } else if ((position != 0) && (nextReqM.start() < nextReachM.end())) { + fail("Next request before previous reached"); + } else if (!nextReachM.find()) { + fail("Didn't find next phase reached"); + } else if (nextReachM.start() <= nextReqM.end()) { + fail("Next request/reached misordered"); + } + + // Find the expected request message, and ensure it is the next. + Pattern reqP = Pattern.compile(requestPrefix + phaseName); + Matcher reqM = reqP.matcher(messages); + if (!reqM.find(position)) { + fail("Didn't find request for " + phaseName); + } else if (reqM.start() != nextReqM.start()) { + fail("Request mis-positioned for " + phaseName); + } + + // Find the expected reached message, and ensure it is the next. + Pattern reachP = Pattern.compile(reachedPrefix + phaseName); + Matcher reachM = reachP.matcher(messages); + if (!reachM.find(position)) { + fail("Didn't find reached for " + phaseName); + } else if (reachM.start() != nextReachM.start()) { + fail("Reached mis-positioned for " + phaseName); + } + + // If there is a pending log message (see below), ensure + // it was before the current reached message. + if (pendingPhaseMessage != null) { + if (pendingPhaseMessagePosition >= reachM.start()) { + fail("Log message after next reached message: " + + pendingPhaseMessage); + } + } + + // If the phase has an associated logging message, verify + // such a logging message is present following the + // request, and otherwise positioned appropriately. The + // complication here is that the logging message + // associated with a request might follow the reached + // message, and even the next request message, if there is + // a later request. But it must preceed the next + // logging message and the next reached message. + boolean clearPendingPhaseMessage = true; + if (phaseMsg != null) { + Pattern logP = Pattern.compile("GC\\(\\d+\\)\\s+" + phaseMsg); + Matcher logM = logP.matcher(messages); + if (!logM.find(reqM.end())) { + fail("Didn't find message " + phaseMsg); + } + + if (pendingPhaseMessage != null) { + if (pendingPhaseMessagePosition >= logM.start()) { + fail("Log messages out of order: " + + pendingPhaseMessage + " should preceed " + + phaseMsg); + } + } + + if (reachM.end() <= logM.start()) { + clearPendingPhaseMessage = false; + pendingPhaseMessage = phaseMsg; + pendingPhaseMessagePosition = logM.end(); + } + } + if (clearPendingPhaseMessage) { + pendingPhaseMessage = null; + pendingPhaseMessagePosition = -1; + } + + // Update position for start of next phase search. + position = reachM.end(); + } + // It's okay for there to be a leftover pending phase message. + // We know it was found before the end of the log. + } + + private static final class Executor { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static void step(String phase) { + System.out.println(requestPrefix + phase); + WB.requestConcurrentGCPhase(phase); + System.out.println(reachedPrefix + phase); + } + + public static void main(String[] phases) throws Exception { + // Iterate through set sequence of phases, reporting each. + for (String phase: phases) { + step(phase); + } + // Wait a little to allow a delayed logging message for + // the final request/reached to be printed before exiting + // the program. + Thread.sleep(250); + } + } +} diff -r 24999171edf9 -r dacebddcdea0 hotspot/test/gc/concurrent_phase_control/CheckSupported.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/concurrent_phase_control/CheckSupported.java Thu Apr 13 16:38:39 2017 -0400 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package gc.concurrent_phase_control; + +/* + * Utility class that provides verification of expected behavior of + * the Concurrent GC Phase Control WB API when the current GC supports + * phase control. The invoking test must provide WhiteBox access. + */ + +import sun.hotspot.WhiteBox; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class CheckSupported { + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static Set toSet(List list, String which) + throws Exception + { + Set result = new HashSet(list); + if (result.size() < list.size()) { + throw new RuntimeException(which + " phases contains duplicates"); + } + return result; + } + + private static void checkPhases(String[] expectedPhases) throws Exception { + String[] actualPhases = WB.getConcurrentGCPhases(); + + List expectedList = Arrays.asList(expectedPhases); + List actualList = Arrays.asList(actualPhases); + + Set expected = toSet(expectedList, "Expected"); + Set actual = toSet(actualList, "Actual"); + + expected.removeAll(actualList); + actual.removeAll(expectedList); + + boolean match = true; + if (!expected.isEmpty()) { + match = false; + System.out.println("Unexpected phases:"); + for (String s: expected) { + System.out.println(" " + s); + } + } + if (!actual.isEmpty()) { + match = false; + System.out.println("Expected but missing phases:"); + for (String s: actual) { + System.out.println(" " + s); + } + } + if (!match) { + throw new RuntimeException("Mismatch between expected and actual phases"); + } + } + + public static void check(String gcName, String[] phases) throws Exception { + // Verify supported. + if (!WB.supportsConcurrentGCPhaseControl()) { + throw new RuntimeException( + gcName + " unexpectedly missing phase control support"); + } + + checkPhases(phases); + + // Verify IllegalArgumentException thrown by request attempt + // with unknown phase. + boolean illegalArgumentThrown = false; + try { + WB.requestConcurrentGCPhase("UNKNOWN PHASE"); + } catch (IllegalArgumentException e) { + // Expected. + illegalArgumentThrown = true; + } catch (Exception e) { + throw new RuntimeException( + gcName + ": Unexpected exception when requesting unknown phase: " + e.toString()); + } + if (!illegalArgumentThrown) { + throw new RuntimeException( + gcName + ": No exception when requesting unknown phase"); + } + } +} + diff -r 24999171edf9 -r dacebddcdea0 hotspot/test/gc/concurrent_phase_control/CheckUnsupported.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/concurrent_phase_control/CheckUnsupported.java Thu Apr 13 16:38:39 2017 -0400 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package gc.concurrent_phase_control; + +/* + * Utility class that provides verification of expected behavior of + * the Concurrent GC Phase Control WB API when the current GC does not + * support phase control. The invoking test must provide WhiteBox access. + */ + +import sun.hotspot.WhiteBox; + +public class CheckUnsupported { + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + public static void check(String gcName) throws Exception { + // Verify unsupported. + if (WB.supportsConcurrentGCPhaseControl()) { + throw new RuntimeException( + gcName + " unexpectedly supports phase control"); + } + + // Verify phase sequence is empty. + String[] phases = WB.getConcurrentGCPhases(); + if (phases.length > 0) { + throw new RuntimeException( + gcName + " unexpectedly has non-empty phases"); + } + + // Verify IllegalStateException thrown by request attempt. + boolean illegalStateThrown = false; + try { + WB.requestConcurrentGCPhase("UNKNOWN PHASE"); + } catch (IllegalStateException e) { + // Expected. + illegalStateThrown = true; + } catch (Exception e) { + throw new RuntimeException( + gcName + ": Unexpected exception when requesting phase: " + + e.toString()); + } + if (!illegalStateThrown) { + throw new RuntimeException( + gcName + ": No exception when requesting phase"); + } + } +} + diff -r 24999171edf9 -r dacebddcdea0 hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlCMS.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlCMS.java Thu Apr 13 16:38:39 2017 -0400 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test TestConcurrentPhaseControlCMS + * @bug 8169517 + * @requires vm.gc.ConcMarkSweep + * @summary Verify CMS GC doesn't support WhiteBox concurrent phase control. + * @key gc + * @modules java.base + * @library /test/lib / + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -XX:+UseConcMarkSweepGC + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * TestConcurrentPhaseControlCMS + */ + +import gc.concurrent_phase_control.CheckUnsupported; + +public class TestConcurrentPhaseControlCMS { + + public static void main(String[] args) throws Exception { + CheckUnsupported.check("CMS"); + } +} diff -r 24999171edf9 -r dacebddcdea0 hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlG1.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlG1.java Thu Apr 13 16:38:39 2017 -0400 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test TestConcurrentPhaseControlG1 + * @bug 8169517 + * @requires vm.gc.G1 + * @summary Test of WhiteBox concurrent GC phase control for G1. + * @key gc + * @modules java.base + * @library /test/lib / + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run driver TestConcurrentPhaseControlG1 + */ + +import gc.concurrent_phase_control.CheckControl; + +public class TestConcurrentPhaseControlG1 { + + // Pairs of phase name and regex to match log stringm for stepping through, + private static final String[][] g1PhaseInfo = { + // Step through the phases in order. + {"IDLE", null}, + {"CONCURRENT_CYCLE", "Concurrent Cycle"}, + {"IDLE", null}, // Resume IDLE before testing subphases + {"CLEAR_CLAIMED_MARKS", "Concurrent Clear Claimed Marks"}, + {"SCAN_ROOT_REGIONS", "Concurrent Scan Root Regions"}, + // ^F so not "From Roots", ^R so not "Restart" + {"CONCURRENT_MARK", "Concurrent Mark [^FR]"}, + {"IDLE", null}, // Resume IDLE before testing subphases + {"MARK_FROM_ROOTS", "Concurrent Mark From Roots"}, + {"BEFORE_REMARK", null}, + {"REMARK", "Pause Remark"}, + {"CREATE_LIVE_DATA", "Concurrent Create Live Data"}, + // "COMPLETE_CLEANUP", -- optional phase, not reached by this test + {"CLEANUP_FOR_NEXT_MARK", "Concurrent Cleanup for Next Mark"}, + // Clear request + {"IDLE", null}, + {"ANY", null}, + // Request a phase. + {"MARK_FROM_ROOTS", "Concurrent Mark From Roots"}, + // Request an earlier phase, to ensure loop rather than stuck at idle. + {"SCAN_ROOT_REGIONS", "Concurrent Scan Root Regions"}, + // Clear request, to unblock service. + {"IDLE", null}, + {"ANY", null}, + }; + + private static final String[] g1Options = + new String[]{"-XX:+UseG1GC", "-Xlog:gc,gc+marking"}; + + private static final String g1Name = "G1"; + + public static void main(String[] args) throws Exception { + CheckControl.check(g1Name, g1Options, g1PhaseInfo); + } +} diff -r 24999171edf9 -r dacebddcdea0 hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlG1Basics.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlG1Basics.java Thu Apr 13 16:38:39 2017 -0400 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test TestConcurrentPhaseControlG1Basics + * @bug 8169517 + * @requires vm.gc.G1 + * @summary Verify G1 supports concurrent phase control and has the + * expected set of phases. + * @key gc + * @modules java.base + * @library /test/lib / + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -XX:+UseG1GC + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * TestConcurrentPhaseControlG1Basics + */ + +import gc.concurrent_phase_control.CheckSupported; + +public class TestConcurrentPhaseControlG1Basics { + + private static final String[] phases = { + "ANY", + "IDLE", + "CONCURRENT_CYCLE", + "CLEAR_CLAIMED_MARKS", + "SCAN_ROOT_REGIONS", + "CONCURRENT_MARK", + "MARK_FROM_ROOTS", + "BEFORE_REMARK", + "REMARK", + "CREATE_LIVE_DATA", + "COMPLETE_CLEANUP", + "CLEANUP_FOR_NEXT_MARK", + }; + + public static void main(String[] args) throws Exception { + CheckSupported.check("G1", phases); + } +} diff -r 24999171edf9 -r dacebddcdea0 hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlParallel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlParallel.java Thu Apr 13 16:38:39 2017 -0400 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test TestConcurrentPhaseControlParallel + * @bug 8169517 + * @requires vm.gc.Parallel + * @summary Verify Parallel GC doesn't support WhiteBox concurrent phase control. + * @key gc + * @modules java.base + * @library /test/lib / + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -XX:+UseParallelGC + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * TestConcurrentPhaseControlParallel + */ + +import gc.concurrent_phase_control.CheckUnsupported; + +public class TestConcurrentPhaseControlParallel { + + public static void main(String[] args) throws Exception { + CheckUnsupported.check("Parallel"); + } +} diff -r 24999171edf9 -r dacebddcdea0 hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlSerial.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/concurrent_phase_control/TestConcurrentPhaseControlSerial.java Thu Apr 13 16:38:39 2017 -0400 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test TestConcurrentPhaseControlSerial + * @bug 8169517 + * @requires vm.gc.Serial + * @summary Verify Serial GC doesn't support WhiteBox concurrent phase control. + * @key gc + * @modules java.base + * @library /test/lib / + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -XX:+UseSerialGC + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * TestConcurrentPhaseControlSerial + */ + +import gc.concurrent_phase_control.CheckUnsupported; + +public class TestConcurrentPhaseControlSerial { + + public static void main(String[] args) throws Exception { + CheckUnsupported.check("Serial"); + } +}