From 9f090cb6f85d0f13c1061cb870d5901350ba3407 Mon Sep 17 00:00:00 2001 From: Paul Hohensee Date: Thu, 13 Aug 2020 11:31:37 -0700 Subject: [PATCH 1/2] =?UTF-8?q?8215624:=20Add=20parallel=20heap=20iteratio?= =?UTF-8?q?n=20for=20jmap=20=E2=80=93histo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chunk and parallelize the heap scan Reviewed-by: sspitsyn, stefank, phh --- src/hotspot/share/gc/epsilon/epsilonHeap.hpp | 4 + src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 39 ++++++- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 13 ++- .../gc/parallel/parallelScavengeHeap.cpp | 5 +- .../gc/parallel/parallelScavengeHeap.hpp | 2 + src/hotspot/share/gc/serial/serialHeap.cpp | 5 + src/hotspot/share/gc/serial/serialHeap.hpp | 4 + src/hotspot/share/gc/shared/collectedHeap.hpp | 14 +++ .../share/gc/shared/gcVMOperations.cpp | 2 +- .../share/gc/shared/gcVMOperations.hpp | 7 +- .../share/gc/shenandoah/shenandoahHeap.cpp | 4 + .../share/gc/shenandoah/shenandoahHeap.hpp | 2 + src/hotspot/share/gc/z/zCollectedHeap.cpp | 4 + src/hotspot/share/gc/z/zCollectedHeap.hpp | 2 + src/hotspot/share/gc/z/zHeap.cpp | 21 ++++ src/hotspot/share/gc/z/zHeap.hpp | 1 + src/hotspot/share/memory/heapInspection.cpp | 95 +++++++++++++-- src/hotspot/share/memory/heapInspection.hpp | 44 ++++++- src/hotspot/share/runtime/arguments.hpp | 12 +- src/hotspot/share/services/attachListener.cpp | 31 +++-- .../share/classes/sun/tools/jmap/JMap.java | 20 +++- test/jdk/sun/tools/jmap/BasicJMapTest.java | 109 +++++++++++------- 22 files changed, 358 insertions(+), 82 deletions(-) diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp index f08c67a3c89..cf9b19bf0ea 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp @@ -119,6 +119,10 @@ public: // No GC threads virtual void gc_threads_do(ThreadClosure* tc) const {} + // Runs the given AbstractGangTask with the current active workers + // No workGang for EpsilonHeap, work serially with thread 0 + virtual void run_task(AbstractGangTask* task) { task->work(0); } + // No nmethod handling virtual void register_nmethod(nmethod* nm) {} virtual void unregister_nmethod(nmethod* nm) {} diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index d97463c406f..dc38214c6a7 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -89,6 +89,7 @@ #include "logging/log.hpp" #include "memory/allocation.hpp" #include "memory/iterator.hpp" +#include "memory/heapInspection.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/access.inline.hpp" @@ -161,9 +162,13 @@ void G1RegionMappingChangedListener::on_commit(uint start_idx, size_t num_region reset_from_card_cache(start_idx, num_regions); } -Tickspan G1CollectedHeap::run_task(AbstractGangTask* task) { - Ticks start = Ticks::now(); +void G1CollectedHeap::run_task(AbstractGangTask* task) { workers()->run_task(task, workers()->active_workers()); +} + +Tickspan G1CollectedHeap::run_task_timed(AbstractGangTask* task) { + Ticks start = Ticks::now(); + run_task(task); return Ticks::now() - start; } @@ -2301,6 +2306,30 @@ void G1CollectedHeap::object_iterate(ObjectClosure* cl) { heap_region_iterate(&blk); } +class G1ParallelObjectIterator : public ParallelObjectIterator { +private: + G1CollectedHeap* _heap; + HeapRegionClaimer _claimer; + +public: + G1ParallelObjectIterator(uint thread_num) : + _heap(G1CollectedHeap::heap()), + _claimer(thread_num == 0 ? G1CollectedHeap::heap()->workers()->active_workers() : thread_num) {} + + virtual void object_iterate(ObjectClosure* cl, uint worker_id) { + _heap->object_iterate_parallel(cl, worker_id, &_claimer); + } +}; + +ParallelObjectIterator* G1CollectedHeap::parallel_object_iterator(uint thread_num) { + return new G1ParallelObjectIterator(thread_num); +} + +void G1CollectedHeap::object_iterate_parallel(ObjectClosure* cl, uint worker_id, HeapRegionClaimer* claimer) { + IterateObjectClosureRegionClosure blk(cl); + heap_region_par_iterate_from_worker_offset(&blk, claimer, worker_id); +} + void G1CollectedHeap::keep_alive(oop obj) { G1BarrierSet::enqueue(obj); } @@ -3694,7 +3723,7 @@ void G1CollectedHeap::pre_evacuate_collection_set(G1EvacuationInfo& evacuation_i { G1PrepareEvacuationTask g1_prep_task(this); - Tickspan task_time = run_task(&g1_prep_task); + Tickspan task_time = run_task_timed(&g1_prep_task); phase_times()->record_register_regions(task_time.seconds() * 1000.0, g1_prep_task.humongous_total(), @@ -3843,7 +3872,7 @@ void G1CollectedHeap::evacuate_initial_collection_set(G1ParScanThreadStateSet* p { G1RootProcessor root_processor(this, num_workers); G1EvacuateRegionsTask g1_par_task(this, per_thread_states, _task_queues, &root_processor, num_workers); - task_time = run_task(&g1_par_task); + task_time = run_task_timed(&g1_par_task); // Closing the inner scope will execute the destructor for the G1RootProcessor object. // To extract its code root fixup time we measure total time of this scope and // subtract from the time the WorkGang task took. @@ -3882,7 +3911,7 @@ void G1CollectedHeap::evacuate_next_optional_regions(G1ParScanThreadStateSet* pe { G1MarkScope code_mark_scope; G1EvacuateOptionalRegionsTask task(per_thread_states, _task_queues, workers()->active_workers()); - task_time = run_task(&task); + task_time = run_task_timed(&task); // See comment in evacuate_collection_set() for the reason of the scope. } Tickspan total_processing = Ticks::now() - start_processing; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 7aeb775df4d..7c9938c8174 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -551,9 +551,12 @@ public: WorkGang* workers() const { return _workers; } - // Runs the given AbstractGangTask with the current active workers, returning the - // total time taken. - Tickspan run_task(AbstractGangTask* task); + // Runs the given AbstractGangTask with the current active workers. + virtual void run_task(AbstractGangTask* task); + + // Runs the given AbstractGangTask with the current active workers, + // returning the total time taken. + Tickspan run_task_timed(AbstractGangTask* task); G1Allocator* allocator() { return _allocator; @@ -1173,9 +1176,13 @@ public: // Iteration functions. + void object_iterate_parallel(ObjectClosure* cl, uint worker_id, HeapRegionClaimer* claimer); + // Iterate over all objects, calling "cl.do_object" on each. virtual void object_iterate(ObjectClosure* cl); + virtual ParallelObjectIterator* parallel_object_iterator(uint thread_num); + // Keep alive an object that was loaded with AS_NO_KEEPALIVE. virtual void keep_alive(oop obj); diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index edb1ca8a2d0..77e2b4e5442 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -539,7 +539,6 @@ void ParallelScavengeHeap::object_iterate(ObjectClosure* cl) { old_gen()->object_iterate(cl); } - HeapWord* ParallelScavengeHeap::block_start(const void* addr) const { if (young_gen()->is_in_reserved(addr)) { assert(young_gen()->is_in(addr), @@ -611,6 +610,10 @@ void ParallelScavengeHeap::gc_threads_do(ThreadClosure* tc) const { ParallelScavengeHeap::heap()->workers().threads_do(tc); } +void ParallelScavengeHeap::run_task(AbstractGangTask* task) { + _workers.run_task(task); +} + void ParallelScavengeHeap::print_tracing_info() const { AdaptiveSizePolicyOutput::print(); log_debug(gc, heap, exit)("Accumulated young generation GC time %3.7f secs", PSScavenge::accumulated_time()->seconds()); diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index b470d5303f2..30a6a61209a 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -218,6 +218,8 @@ class ParallelScavengeHeap : public CollectedHeap { virtual void print_on(outputStream* st) const; virtual void print_on_error(outputStream* st) const; virtual void gc_threads_do(ThreadClosure* tc) const; + // Runs the given AbstractGangTask with the current active workers. + virtual void run_task(AbstractGangTask* task); virtual void print_tracing_info() const; virtual WorkGang* get_safepoint_workers() { return &_workers; } diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index 4aea11ff702..a2687706a9c 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -87,3 +87,8 @@ GrowableArray SerialHeap::memory_pools() { memory_pools.append(_old_pool); return memory_pools; } + +// No workGang for SerialHeap, work serially with thread 0. +void SerialHeap::run_task(AbstractGangTask* task) { + task->work(0); +} diff --git a/src/hotspot/share/gc/serial/serialHeap.hpp b/src/hotspot/share/gc/serial/serialHeap.hpp index 06b326fa083..f30e1282725 100644 --- a/src/hotspot/share/gc/serial/serialHeap.hpp +++ b/src/hotspot/share/gc/serial/serialHeap.hpp @@ -75,6 +75,10 @@ public: template void oop_since_save_marks_iterate(OopClosureType1* cur, OopClosureType2* older); + + // Runs the given AbstractGangTask with the current active workers. + // No workGang for SerialHeap, work serially with thread 0. + virtual void run_task(AbstractGangTask* task); }; #endif // SHARE_GC_SERIAL_SERIALHEAP_HPP diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp index b9675eb75b2..c7631f19aee 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp @@ -29,6 +29,7 @@ #include "gc/shared/gcWhen.hpp" #include "gc/shared/verifyOption.hpp" #include "memory/allocation.hpp" +#include "memory/heapInspection.hpp" #include "memory/universe.hpp" #include "runtime/handles.hpp" #include "runtime/perfData.hpp" @@ -44,6 +45,7 @@ // class defines the functions that a heap must implement, and contains // infrastructure common to all heaps. +class AbstractGangTask; class AdaptiveSizePolicy; class BarrierSet; class GCHeapSummary; @@ -85,6 +87,11 @@ class GCHeapLog : public EventLogBase { } }; +class ParallelObjectIterator : public CHeapObj { +public: + virtual void object_iterate(ObjectClosure* cl, uint worker_id) = 0; +}; + // // CollectedHeap // GenCollectedHeap @@ -407,6 +414,10 @@ class CollectedHeap : public CHeapObj { // Iterate over all objects, calling "cl.do_object" on each. virtual void object_iterate(ObjectClosure* cl) = 0; + virtual ParallelObjectIterator* parallel_object_iterator(uint thread_num) { + return NULL; + } + // Keep alive an object that was loaded with AS_NO_KEEPALIVE. virtual void keep_alive(oop obj) {} @@ -456,6 +467,9 @@ class CollectedHeap : public CHeapObj { // Iterator for all GC threads (other than VM thread) virtual void gc_threads_do(ThreadClosure* tc) const = 0; + // Run given task. Possibly in parallel if the GC supports it. + virtual void run_task(AbstractGangTask* task) = 0; + // Print any relevant tracing info that flags imply. // Default implementation does nothing. virtual void print_tracing_info() const = 0; diff --git a/src/hotspot/share/gc/shared/gcVMOperations.cpp b/src/hotspot/share/gc/shared/gcVMOperations.cpp index d046e79347c..2bf4d3866ad 100644 --- a/src/hotspot/share/gc/shared/gcVMOperations.cpp +++ b/src/hotspot/share/gc/shared/gcVMOperations.cpp @@ -149,7 +149,7 @@ void VM_GC_HeapInspection::doit() { } } HeapInspection inspect; - inspect.heap_inspection(_out); + inspect.heap_inspection(_out, _parallel_thread_num); } diff --git a/src/hotspot/share/gc/shared/gcVMOperations.hpp b/src/hotspot/share/gc/shared/gcVMOperations.hpp index 4b91c7cf51e..fe269f32c0e 100644 --- a/src/hotspot/share/gc/shared/gcVMOperations.hpp +++ b/src/hotspot/share/gc/shared/gcVMOperations.hpp @@ -125,12 +125,15 @@ class VM_GC_HeapInspection: public VM_GC_Operation { private: outputStream* _out; bool _full_gc; + uint _parallel_thread_num; public: - VM_GC_HeapInspection(outputStream* out, bool request_full_gc) : + VM_GC_HeapInspection(outputStream* out, bool request_full_gc, + uint parallel_thread_num = 1) : VM_GC_Operation(0 /* total collections, dummy, ignored */, GCCause::_heap_inspection /* GC Cause */, 0 /* total full collections, dummy, ignored */, - request_full_gc), _out(out), _full_gc(request_full_gc) {} + request_full_gc), _out(out), _full_gc(request_full_gc), + _parallel_thread_num(parallel_thread_num) {} ~VM_GC_HeapInspection() {} virtual VMOp_Type type() const { return VMOp_GC_HeapInspection; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 58333f6c56e..7b8b38ac646 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1195,6 +1195,10 @@ void ShenandoahHeap::gc_threads_do(ThreadClosure* tcl) const { } } +void ShenandoahHeap::run_task(AbstractGangTask* task) { + workers()->run_task(task, workers()->active_workers()); +} + void ShenandoahHeap::print_tracing_info() const { LogTarget(Info, gc, stats) lt; if (lt.is_enabled()) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 72d9450336c..dabf8938a55 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -198,6 +198,8 @@ public: WorkGang* get_safepoint_workers(); void gc_threads_do(ThreadClosure* tcl) const; + // Runs the given AbstractGangTask with the current active workers. + virtual void run_task(AbstractGangTask* task); // ---------- Heap regions handling machinery // diff --git a/src/hotspot/share/gc/z/zCollectedHeap.cpp b/src/hotspot/share/gc/z/zCollectedHeap.cpp index ce6b196c28b..1277bfc74fb 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.cpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.cpp @@ -253,6 +253,10 @@ void ZCollectedHeap::object_iterate(ObjectClosure* cl) { _heap.object_iterate(cl, true /* visit_weaks */); } +void ZCollectedHeap::run_task(AbstractGangTask* task) { + return _heap.run_task(task); +} + void ZCollectedHeap::keep_alive(oop obj) { _heap.keep_alive(obj); } diff --git a/src/hotspot/share/gc/z/zCollectedHeap.hpp b/src/hotspot/share/gc/z/zCollectedHeap.hpp index b54c70c083d..030a470879c 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.hpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.hpp @@ -98,6 +98,8 @@ public: virtual void object_iterate(ObjectClosure* cl); + virtual void run_task(AbstractGangTask* task); + virtual void keep_alive(oop obj); virtual void register_nmethod(nmethod* nm); diff --git a/src/hotspot/share/gc/z/zHeap.cpp b/src/hotspot/share/gc/z/zHeap.cpp index 73bdd1dea84..9fbad8d6e8f 100644 --- a/src/hotspot/share/gc/z/zHeap.cpp +++ b/src/hotspot/share/gc/z/zHeap.cpp @@ -35,6 +35,7 @@ #include "gc/z/zRelocationSetSelector.inline.hpp" #include "gc/z/zResurrection.hpp" #include "gc/z/zStat.hpp" +#include "gc/z/zTask.hpp" #include "gc/z/zThread.inline.hpp" #include "gc/z/zVerify.hpp" #include "gc/z/zWorkers.inline.hpp" @@ -185,6 +186,26 @@ void ZHeap::threads_do(ThreadClosure* tc) const { _workers.threads_do(tc); } +// Adapter class from AbstractGangTask to Ztask +class ZAbstractGangTaskAdapter : public ZTask { +private: + AbstractGangTask* _task; + +public: + ZAbstractGangTaskAdapter(AbstractGangTask* task) : + ZTask(task->name()), + _task(task) { } + + virtual void work() { + _task->work(ZThread::worker_id()); + } +}; + +void ZHeap::run_task(AbstractGangTask* task) { + ZAbstractGangTaskAdapter ztask(task); + _workers.run_parallel(&ztask); +} + void ZHeap::out_of_memory() { ResourceMark rm; diff --git a/src/hotspot/share/gc/z/zHeap.hpp b/src/hotspot/share/gc/z/zHeap.hpp index f8505274ebb..fd1c87bf772 100644 --- a/src/hotspot/share/gc/z/zHeap.hpp +++ b/src/hotspot/share/gc/z/zHeap.hpp @@ -98,6 +98,7 @@ public: uint nconcurrent_no_boost_worker_threads() const; void set_boost_worker_threads(bool boost); void threads_do(ThreadClosure* tc) const; + void run_task(AbstractGangTask* task); // Reference processing ReferenceDiscoverer* reference_discoverer(); diff --git a/src/hotspot/share/memory/heapInspection.cpp b/src/hotspot/share/memory/heapInspection.cpp index ef3a9e71384..20235b7a7e9 100644 --- a/src/hotspot/share/memory/heapInspection.cpp +++ b/src/hotspot/share/memory/heapInspection.cpp @@ -35,6 +35,7 @@ #include "memory/universe.hpp" #include "oops/oop.inline.hpp" #include "oops/reflectionAccessorImplKlassHelper.hpp" +#include "runtime/atomic.hpp" #include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" @@ -237,6 +238,41 @@ size_t KlassInfoTable::size_of_instances_in_words() const { return _size_of_instances_in_words; } +// Return false if the entry could not be recorded on account +// of running out of space required to create a new entry. +bool KlassInfoTable::merge_entry(const KlassInfoEntry* cie) { + Klass* k = cie->klass(); + KlassInfoEntry* elt = lookup(k); + // elt may be NULL if it's a new klass for which we + // could not allocate space for a new entry in the hashtable. + if (elt != NULL) { + elt->set_count(elt->count() + cie->count()); + elt->set_words(elt->words() + cie->words()); + _size_of_instances_in_words += cie->words(); + return true; + } + return false; +} + +class KlassInfoTableMergeClosure : public KlassInfoClosure { +private: + KlassInfoTable* _dest; + bool _success; +public: + KlassInfoTableMergeClosure(KlassInfoTable* table) : _dest(table), _success(true) {} + void do_cinfo(KlassInfoEntry* cie) { + _success &= _dest->merge_entry(cie); + } + bool success() { return _success; } +}; + +// merge from table +bool KlassInfoTable::merge(KlassInfoTable* table) { + KlassInfoTableMergeClosure closure(this); + table->iterate(&closure); + return closure.success(); +} + int KlassInfoHisto::sort_helper(KlassInfoEntry** e1, KlassInfoEntry** e2) { return (*e1)->compare(*e1,*e2); } @@ -482,7 +518,7 @@ class HistoClosure : public KlassInfoClosure { class RecordInstanceClosure : public ObjectClosure { private: KlassInfoTable* _cit; - size_t _missed_count; + uintx _missed_count; BoolObjectClosure* _filter; public: RecordInstanceClosure(KlassInfoTable* cit, BoolObjectClosure* filter) : @@ -496,7 +532,7 @@ class RecordInstanceClosure : public ObjectClosure { } } - size_t missed_count() { return _missed_count; } + uintx missed_count() { return _missed_count; } private: bool should_visit(oop obj) { @@ -504,23 +540,68 @@ class RecordInstanceClosure : public ObjectClosure { } }; -size_t HeapInspection::populate_table(KlassInfoTable* cit, BoolObjectClosure *filter) { - ResourceMark rm; +// Heap inspection for every worker. +// When native OOM happens for KlassInfoTable, set _success to false. +void ParHeapInspectTask::work(uint worker_id) { + uintx missed_count = 0; + bool merge_success = true; + if (!Atomic::load(&_success)) { + // other worker has failed on parallel iteration. + return; + } + KlassInfoTable cit(false); + if (cit.allocation_failed()) { + // fail to allocate memory, stop parallel mode + Atomic::store(&_success, false); + return; + } + RecordInstanceClosure ric(&cit, _filter); + _poi->object_iterate(&ric, worker_id); + missed_count = ric.missed_count(); + { + MutexLocker x(&_mutex); + merge_success = _shared_cit->merge(&cit); + } + if (merge_success) { + Atomic::add(&_missed_count, missed_count); + } else { + Atomic::store(&_success, false); + } +} + +uintx HeapInspection::populate_table(KlassInfoTable* cit, BoolObjectClosure *filter, uint parallel_thread_num) { + + // Try parallel first. + if (parallel_thread_num > 1) { + ResourceMark rm; + ParallelObjectIterator* poi = Universe::heap()->parallel_object_iterator(parallel_thread_num); + if (poi != NULL) { + ParHeapInspectTask task(poi, cit, filter); + Universe::heap()->run_task(&task); + delete poi; + if (task.success()) { + return task.missed_count(); + } + } + } + + ResourceMark rm; + // If no parallel iteration available, run serially. RecordInstanceClosure ric(cit, filter); Universe::heap()->object_iterate(&ric); return ric.missed_count(); } -void HeapInspection::heap_inspection(outputStream* st) { +void HeapInspection::heap_inspection(outputStream* st, uint parallel_thread_num) { ResourceMark rm; KlassInfoTable cit(false); if (!cit.allocation_failed()) { // populate table with object allocation info - size_t missed_count = populate_table(&cit); + uintx missed_count = populate_table(&cit, NULL, parallel_thread_num); if (missed_count != 0) { - log_info(gc, classhisto)("WARNING: Ran out of C-heap; undercounted " SIZE_FORMAT + log_info(gc, classhisto)("WARNING: Ran out of C-heap; undercounted " UINTX_FORMAT " total instances in data below", missed_count); } diff --git a/src/hotspot/share/memory/heapInspection.hpp b/src/hotspot/share/memory/heapInspection.hpp index 995077ae497..3057ba31728 100644 --- a/src/hotspot/share/memory/heapInspection.hpp +++ b/src/hotspot/share/memory/heapInspection.hpp @@ -30,6 +30,9 @@ #include "oops/oop.hpp" #include "oops/annotations.hpp" #include "utilities/macros.hpp" +#include "gc/shared/workgroup.hpp" + +class ParallelObjectIterator; #if INCLUDE_SERVICES @@ -122,6 +125,8 @@ class KlassInfoTable: public StackObj { void iterate(KlassInfoClosure* cic); bool allocation_failed() { return _buckets == NULL; } size_t size_of_instances_in_words() const; + bool merge(KlassInfoTable* table); + bool merge_entry(const KlassInfoEntry* cie); friend class KlassInfoHisto; friend class KlassHierarchy; @@ -211,11 +216,46 @@ class KlassInfoClosure; class HeapInspection : public StackObj { public: - void heap_inspection(outputStream* st) NOT_SERVICES_RETURN; - size_t populate_table(KlassInfoTable* cit, BoolObjectClosure* filter = NULL) NOT_SERVICES_RETURN_(0); + void heap_inspection(outputStream* st, uint parallel_thread_num = 1) NOT_SERVICES_RETURN; + uintx populate_table(KlassInfoTable* cit, BoolObjectClosure* filter = NULL, uint parallel_thread_num = 1) NOT_SERVICES_RETURN_(0); static void find_instances_at_safepoint(Klass* k, GrowableArray* result) NOT_SERVICES_RETURN; private: void iterate_over_heap(KlassInfoTable* cit, BoolObjectClosure* filter = NULL); }; +// Parallel heap inspection task. Parallel inspection can fail due to +// a native OOM when allocating memory for TL-KlassInfoTable. +// _success will be set false on an OOM, and serial inspection tried. +class ParHeapInspectTask : public AbstractGangTask { + private: + ParallelObjectIterator* _poi; + KlassInfoTable* _shared_cit; + BoolObjectClosure* _filter; + uintx _missed_count; + bool _success; + Mutex _mutex; + + public: + ParHeapInspectTask(ParallelObjectIterator* poi, + KlassInfoTable* shared_cit, + BoolObjectClosure* filter) : + AbstractGangTask("Iterating heap"), + _poi(poi), + _shared_cit(shared_cit), + _filter(filter), + _missed_count(0), + _success(true), + _mutex(Mutex::leaf, "Parallel heap iteration data merge lock") {} + + uintx missed_count() const { + return _missed_count; + } + + bool success() { + return _success; + } + + virtual void work(uint worker_id); +}; + #endif // SHARE_MEMORY_HEAPINSPECTION_HPP diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index c652ee3f9fe..2be3d3ad746 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -449,12 +449,6 @@ class Arguments : AllStatic { static ArgsRange check_memory_size(julong size, julong min_size, julong max_size); static ArgsRange parse_memory_size(const char* s, julong* long_arg, julong min_size, julong max_size = max_uintx); - // Parse a string for a unsigned integer. Returns true if value - // is an unsigned integer greater than or equal to the minimum - // parameter passed and returns the value in uintx_arg. Returns - // false otherwise, with uintx_arg undefined. - static bool parse_uintx(const char* value, uintx* uintx_arg, - uintx min_size); // methods to build strings from individual args static void build_jvm_args(const char* arg); @@ -498,6 +492,12 @@ class Arguments : AllStatic { public: // Parses the arguments, first phase static jint parse(const JavaVMInitArgs* args); + // Parse a string for a unsigned integer. Returns true if value + // is an unsigned integer greater than or equal to the minimum + // parameter passed and returns the value in uintx_arg. Returns + // false otherwise, with uintx_arg undefined. + static bool parse_uintx(const char* value, uintx* uintx_arg, + uintx min_size); // Apply ergonomics static jint apply_ergo(); // Adjusts the arguments after the OS have adjusted the arguments diff --git a/src/hotspot/share/services/attachListener.cpp b/src/hotspot/share/services/attachListener.cpp index ce189b3a92a..d8ab4dbcd9c 100644 --- a/src/hotspot/share/services/attachListener.cpp +++ b/src/hotspot/share/services/attachListener.cpp @@ -248,11 +248,13 @@ jint dump_heap(AttachOperation* op, outputStream* out) { // Input arguments :- // arg0: "-live" or "-all" // arg1: Name of the dump file or NULL +// arg2: parallel thread number static jint heap_inspection(AttachOperation* op, outputStream* out) { bool live_objects_only = true; // default is true to retain the behavior before this change is made outputStream* os = out; // if path not specified or path is NULL, use out fileStream* fs = NULL; const char* arg0 = op->arg(0); + uint parallel_thread_num = MAX2(1, (uint)os::initial_active_processor_count() * 3 / 8); if (arg0 != NULL && (strlen(arg0) > 0)) { if (strcmp(arg0, "-all") != 0 && strcmp(arg0, "-live") != 0) { out->print_cr("Invalid argument to inspectheap operation: %s", arg0); @@ -262,21 +264,26 @@ static jint heap_inspection(AttachOperation* op, outputStream* out) { } const char* path = op->arg(1); - if (path != NULL) { - if (path[0] == '\0') { - out->print_cr("No dump file specified"); - } else { - // create file - fs = new (ResourceObj::C_HEAP, mtInternal) fileStream(path); - if (fs == NULL) { - out->print_cr("Failed to allocate space for file: %s", path); - return JNI_ERR; - } - os = fs; + if (path != NULL && path[0] != '\0') { + // create file + fs = new (ResourceObj::C_HEAP, mtInternal) fileStream(path); + if (fs == NULL) { + out->print_cr("Failed to allocate space for file: %s", path); } + os = fs; } - VM_GC_HeapInspection heapop(os, live_objects_only /* request full gc */); + const char* num_str = op->arg(2); + if (num_str != NULL && num_str[0] != '\0') { + uintx num; + if (!Arguments::parse_uintx(num_str, &num, 0)) { + out->print_cr("Invalid parallel thread number: [%s]", num_str); + return JNI_ERR; + } + parallel_thread_num = num == 0 ? parallel_thread_num : (uint)num; + } + + VM_GC_HeapInspection heapop(os, live_objects_only /* request full gc */, parallel_thread_num); VMThread::execute(&heapop); if (os != NULL && os != out) { out->print_cr("Heap inspection file created: %s", path); diff --git a/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java b/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java index 1e527dba19b..4ee0d15ecad 100644 --- a/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java +++ b/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java @@ -169,6 +169,7 @@ public class JMap { UnsupportedEncodingException { String liveopt = "-all"; String filename = null; + String parallel = null; String subopts[] = options.split(","); for (int i = 0; i < subopts.length; i++) { @@ -180,9 +181,17 @@ public class JMap { } else if (subopt.startsWith("file=")) { filename = parseFileName(subopt); if (filename == null) { - usage(1); // invalid options or no filename + System.err.println("Fail: invalid option or no file name '" + subopt +"'"); + usage(1); } + } else if (subopt.startsWith("parallel=")) { + parallel = subopt.substring("parallel=".length()); + if (parallel == null) { + System.err.println("Fail: no number provided in option: '" + subopt + "'"); + usage(1); + } } else { + System.err.println("Fail: invalid option: '" + subopt + "'"); usage(1); } } @@ -190,7 +199,7 @@ public class JMap { System.out.flush(); // inspectHeap is not the same as jcmd GC.class_histogram - executeCommandForPid(pid, "inspectheap", liveopt, filename); + executeCommandForPid(pid, "inspectheap", liveopt, filename, parallel); } private static void dump(String pid, String options) @@ -211,7 +220,8 @@ public class JMap { } if (filename == null) { - usage(1); // invalid options or no filename + System.err.println("Fail: invalid option or no file name"); + usage(1); } // dumpHeap is not the same as jcmd GC.heap_dump @@ -287,6 +297,10 @@ public class JMap { System.err.println(" live count only live objects"); System.err.println(" all count all objects in the heap (default if one of \"live\" or \"all\" is not specified)"); System.err.println(" file= dump data to "); + System.err.println(" parallel= parallel threads number for heap iteration:"); + System.err.println(" parallel=0 default behavior, use predefined number of threads"); + System.err.println(" parallel=1 disable parallel heap iteration"); + System.err.println(" parallel= use N threads for parallel heap iteration"); System.err.println(""); System.err.println(" Example: jmap -histo:live,file=/tmp/histo.data "); System.exit(exit); diff --git a/test/jdk/sun/tools/jmap/BasicJMapTest.java b/test/jdk/sun/tools/jmap/BasicJMapTest.java index a4c240a6bf6..078a6aefff7 100644 --- a/test/jdk/sun/tools/jmap/BasicJMapTest.java +++ b/test/jdk/sun/tools/jmap/BasicJMapTest.java @@ -79,40 +79,68 @@ public class BasicJMapTest { output.shouldHaveExitValue(0); } + private static void testHistoParallelZero() throws Exception { + OutputAnalyzer output = jmap("-histo:parallel=0"); + output.shouldHaveExitValue(0); + } + + private static void testHistoParallel() throws Exception { + OutputAnalyzer output = jmap("-histo:parallel=2"); + output.shouldHaveExitValue(0); + } + + private static void testHistoNonParallel() throws Exception { + OutputAnalyzer output = jmap("-histo:parallel=1"); + output.shouldHaveExitValue(0); + } + private static void testHistoToFile() throws Exception { - histoToFile(false); + histoToFile(false, false, 1); } private static void testHistoLiveToFile() throws Exception { - histoToFile(true); + histoToFile(true, false, 1); } private static void testHistoAllToFile() throws Exception { - boolean explicitAll = true; - histoToFile(false, explicitAll); + histoToFile(false, true, 1); } - private static void histoToFile(boolean live) throws Exception { - boolean explicitAll = false; - histoToFile(live, explicitAll); + private static void testHistoFileParallelZero() throws Exception { + histoToFile(false, false, 0); } - private static void histoToFile(boolean live, boolean explicitAll) throws Exception { - if (live == true && explicitAll == true) { + private static void testHistoFileParallel() throws Exception { + histoToFile(false, false, 2); + } + + private static void histoToFile(boolean live, + boolean explicitAll, + int parallelThreadNum) throws Exception { + String liveArg = ""; + String fileArg = ""; + String parArg = "parallel=" + parallelThreadNum; + String allArgs = "-histo:"; + + if (live && explicitAll) { fail("Illegal argument setting for jmap -histo"); } + if (live) { + liveArg = "live,"; + } + if (explicitAll) { + liveArg = "all,"; + } + File file = new File("jmap.histo.file" + System.currentTimeMillis() + ".histo"); if (file.exists()) { file.delete(); } + fileArg = "file=" + file.getName(); + OutputAnalyzer output; - if (live) { - output = jmap("-histo:live,file=" + file.getName()); - } else if (explicitAll == true) { - output = jmap("-histo:all,file=" + file.getName()); - } else { - output = jmap("-histo:file=" + file.getName()); - } + allArgs = allArgs + liveArg + fileArg + ',' + parArg; + output = jmap(allArgs); output.shouldHaveExitValue(0); output.shouldContain("Heap inspection file created"); file.delete(); @@ -129,43 +157,45 @@ public class BasicJMapTest { } private static void testDump() throws Exception { - dump(false); + dump(false, false); } private static void testDumpLive() throws Exception { - dump(true); + dump(true, false); } private static void testDumpAll() throws Exception { - boolean explicitAll = true; - dump(false, explicitAll); - } - - private static void dump(boolean live) throws Exception { - boolean explicitAll = false; - dump(live, explicitAll); + dump(false, true); } private static void dump(boolean live, boolean explicitAll) throws Exception { - if (live == true && explicitAll == true) { - fail("Illegal argument setting for jmap -dump"); + String liveArg = ""; + String fileArg = ""; + String allArgs = "-dump:"; + + if (live && explicitAll) { + fail("Illegal argument setting for jmap -dump"); } - File dump = new File("jmap.dump." + System.currentTimeMillis() + ".hprof"); - if (dump.exists()) { - dump.delete(); - } - OutputAnalyzer output; if (live) { - output = jmap("-dump:live,format=b,file=" + dump.getName()); - } else if (explicitAll == true) { - output = jmap("-dump:all,format=b,file=" + dump.getName()); - } else { - output = jmap("-dump:format=b,file=" + dump.getName()); + liveArg = "live,"; } + if (explicitAll) { + liveArg = "all,"; + } + + File file = new File("jmap.dump" + System.currentTimeMillis() + ".hprof"); + if (file.exists()) { + file.delete(); + } + fileArg = "file=" + file.getName(); + + OutputAnalyzer output; + allArgs = allArgs + liveArg + "format=b," + fileArg; + output = jmap(allArgs); output.shouldHaveExitValue(0); output.shouldContain("Heap dump file created"); - verifyDumpFile(dump); - dump.delete(); + verifyDumpFile(file); + file.delete(); } private static void verifyDumpFile(File dump) { @@ -195,5 +225,4 @@ public class BasicJMapTest { return output; } - } From 473fa820007b59ecd676b292b3423fcd835712db Mon Sep 17 00:00:00 2001 From: Xue-Lei Andrew Fan Date: Thu, 13 Aug 2020 12:31:12 -0700 Subject: [PATCH 2/2] 8250839: Improve test template SSLEngineTemplate with SSLContextTemplate Reviewed-by: ascarpino --- .../net/ssl/templates/SSLEngineTemplate.java | 417 ++++++------------ 1 file changed, 124 insertions(+), 293 deletions(-) diff --git a/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java b/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java index 32470d11e9e..24ff8e08448 100644 --- a/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java +++ b/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, 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 @@ -26,10 +26,15 @@ /* * @test - * @bug 1234567 - * @summary SSLEngine has not yet caused Solaris kernel to panic + * @bug 8250839 + * @summary Improve test template SSLEngineTemplate with SSLContextTemplate + * @build SSLContextTemplate * @run main/othervm SSLEngineTemplate */ +import javax.net.ssl.*; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import java.nio.ByteBuffer; + /** * A SSLEngine usage example which simplifies the presentation * by removing the I/O and multi-threading concerns. @@ -44,231 +49,139 @@ * * When this application runs, notice that several messages * (wrap/unwrap) pass before any application data is consumed or - * produced. (For more information, please see the SSL/TLS - * specifications.) There may several steps for a successful handshake, - * so it's typical to see the following series of operations: - * - * client server message - * ====== ====== ======= - * wrap() ... ClientHello - * ... unwrap() ClientHello - * ... wrap() ServerHello/Certificate - * unwrap() ... ServerHello/Certificate - * wrap() ... ClientKeyExchange - * wrap() ... ChangeCipherSpec - * wrap() ... Finished - * ... unwrap() ClientKeyExchange - * ... unwrap() ChangeCipherSpec - * ... unwrap() Finished - * ... wrap() ChangeCipherSpec - * ... wrap() Finished - * unwrap() ... ChangeCipherSpec - * unwrap() ... Finished + * produced. */ -import javax.net.ssl.*; -import javax.net.ssl.SSLEngineResult.*; -import java.io.*; -import java.security.*; -import java.nio.*; +public class SSLEngineTemplate implements SSLContextTemplate { + private final SSLEngine clientEngine; // client Engine + private final ByteBuffer clientOut; // write side of clientEngine + private final ByteBuffer clientIn; // read side of clientEngine -public class SSLEngineTemplate { + private final SSLEngine serverEngine; // server Engine + private final ByteBuffer serverOut; // write side of serverEngine + private final ByteBuffer serverIn; // read side of serverEngine + + // For data transport, this example uses local ByteBuffers. This + // isn't really useful, but the purpose of this example is to show + // SSLEngine concepts, not how to do network transport. + private final ByteBuffer cTOs; // "reliable" transport client->server + private final ByteBuffer sTOc; // "reliable" transport server->client + + private SSLEngineTemplate() throws Exception { + serverEngine = configureServerEngine( + createServerSSLContext().createSSLEngine()); + + clientEngine = configureClientEngine( + createClientSSLContext().createSSLEngine()); + + // We'll assume the buffer sizes are the same + // between client and server. + SSLSession session = clientEngine.getSession(); + int appBufferMax = session.getApplicationBufferSize(); + int netBufferMax = session.getPacketBufferSize(); + + // We'll make the input buffers a bit bigger than the max needed + // size, so that unwrap()s following a successful data transfer + // won't generate BUFFER_OVERFLOWS. + // + // We'll use a mix of direct and indirect ByteBuffers for + // tutorial purposes only. In reality, only use direct + // ByteBuffers when they give a clear performance enhancement. + clientIn = ByteBuffer.allocate(appBufferMax + 50); + serverIn = ByteBuffer.allocate(appBufferMax + 50); + + cTOs = ByteBuffer.allocateDirect(netBufferMax); + sTOc = ByteBuffer.allocateDirect(netBufferMax); + + clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes()); + serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes()); + } + + // + // Protected methods could be used to customize the test case. + // /* - * Enables logging of the SSLEngine operations. + * Configure the client side engine. */ - private static final boolean logging = true; + protected SSLEngine configureClientEngine(SSLEngine clientEngine) { + clientEngine.setUseClientMode(true); - /* - * Enables the JSSE system debugging system property: - * - * -Djavax.net.debug=all - * - * This gives a lot of low-level information about operations underway, - * including specific handshake messages, and might be best examined - * after gaining some familiarity with this application. - */ - private static final boolean debug = false; + // Get/set parameters if needed + // SSLParameters paramsClient = clientEngine.getSSLParameters(); + // clientEngine.setSSLParameters(paramsClient); - private final SSLContext sslc; - - private SSLEngine clientEngine; // client Engine - private ByteBuffer clientOut; // write side of clientEngine - private ByteBuffer clientIn; // read side of clientEngine - - private SSLEngine serverEngine; // server Engine - private ByteBuffer serverOut; // write side of serverEngine - private ByteBuffer serverIn; // read side of serverEngine - - /* - * For data transport, this example uses local ByteBuffers. This - * isn't really useful, but the purpose of this example is to show - * SSLEngine concepts, not how to do network transport. - */ - private ByteBuffer cTOs; // "reliable" transport client->server - private ByteBuffer sTOc; // "reliable" transport server->client - - /* - * The following is to set up the keystores. - */ - private static final String pathToStores = "../etc"; - private static final String keyStoreFile = "keystore"; - private static final String trustStoreFile = "truststore"; - private static final char[] passphrase = "passphrase".toCharArray(); - - private static final String keyFilename = - System.getProperty("test.src", ".") + "/" + pathToStores + - "/" + keyStoreFile; - private static final String trustFilename = - System.getProperty("test.src", ".") + "/" + pathToStores + - "/" + trustStoreFile; - - /* - * Main entry point for this test. - */ - public static void main(String args[]) throws Exception { - if (debug) { - System.setProperty("javax.net.debug", "all"); - } - - SSLEngineTemplate test = new SSLEngineTemplate(); - test.runTest(); - - System.out.println("Test Passed."); + return clientEngine; } /* - * Create an initialized SSLContext to use for these tests. + * Configure the server side engine. */ - public SSLEngineTemplate() throws Exception { + protected SSLEngine configureServerEngine(SSLEngine serverEngine) { + serverEngine.setUseClientMode(false); + serverEngine.setNeedClientAuth(true); - KeyStore ks = KeyStore.getInstance("JKS"); - KeyStore ts = KeyStore.getInstance("JKS"); + // Get/set parameters if needed + // + // SSLParameters paramsServer = serverEngine.getSSLParameters(); + // serverEngine.setSSLParameters(paramsServer); - ks.load(new FileInputStream(keyFilename), passphrase); - ts.load(new FileInputStream(trustFilename), passphrase); - - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, passphrase); - - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ts); - - SSLContext sslCtx = SSLContext.getInstance("TLS"); - - sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - - sslc = sslCtx; + return serverEngine; } - /* - * Run the test. - * - * Sit in a tight loop, both engines calling wrap/unwrap regardless - * of whether data is available or not. We do this until both engines - * report back they are closed. - * - * The main loop handles all of the I/O phases of the SSLEngine's - * lifetime: - * - * initial handshaking - * application data transfer - * engine closing - * - * One could easily separate these phases into separate - * sections of code. - */ + public static void main(String[] args) throws Exception { + new SSLEngineTemplate().runTest(); + } + + // + // Private methods that used to build the common part of the test. + // + private void runTest() throws Exception { - boolean dataDone = false; - - createSSLEngines(); - createBuffers(); - - // results from client's last operation SSLEngineResult clientResult; - - // results from server's last operation SSLEngineResult serverResult; - /* - * Examining the SSLEngineResults could be much more involved, - * and may alter the overall flow of the application. - * - * For example, if we received a BUFFER_OVERFLOW when trying - * to write to the output pipe, we could reallocate a larger - * pipe, but instead we wait for the peer to drain it. - */ - Exception clientException = null; - Exception serverException = null; + boolean dataDone = false; + while (isOpen(clientEngine) || isOpen(serverEngine)) { + log("================="); - while (!isEngineClosed(clientEngine) - || !isEngineClosed(serverEngine)) { - - log("================"); - - try { - clientResult = clientEngine.wrap(clientOut, cTOs); - log("client wrap: ", clientResult); - } catch (Exception e) { - clientException = e; - System.out.println("Client wrap() threw: " + e.getMessage()); - } - logEngineStatus(clientEngine); + // client wrap + log("---Client Wrap---"); + clientResult = clientEngine.wrap(clientOut, cTOs); + logEngineStatus(clientEngine, clientResult); runDelegatedTasks(clientEngine); - log("----"); - - try { - serverResult = serverEngine.wrap(serverOut, sTOc); - log("server wrap: ", serverResult); - } catch (Exception e) { - serverException = e; - System.out.println("Server wrap() threw: " + e.getMessage()); - } - logEngineStatus(serverEngine); + // server wrap + log("---Server Wrap---"); + serverResult = serverEngine.wrap(serverOut, sTOc); + logEngineStatus(serverEngine, serverResult); runDelegatedTasks(serverEngine); cTOs.flip(); sTOc.flip(); - log("--------"); - - try { - clientResult = clientEngine.unwrap(sTOc, clientIn); - log("client unwrap: ", clientResult); - } catch (Exception e) { - clientException = e; - System.out.println("Client unwrap() threw: " + e.getMessage()); - } - logEngineStatus(clientEngine); + // client unwrap + log("---Client Unwrap---"); + clientResult = clientEngine.unwrap(sTOc, clientIn); + logEngineStatus(clientEngine, clientResult); runDelegatedTasks(clientEngine); - log("----"); - - try { - serverResult = serverEngine.unwrap(cTOs, serverIn); - log("server unwrap: ", serverResult); - } catch (Exception e) { - serverException = e; - System.out.println("Server unwrap() threw: " + e.getMessage()); - } - logEngineStatus(serverEngine); + // server unwrap + log("---Server Unwrap---"); + serverResult = serverEngine.unwrap(cTOs, serverIn); + logEngineStatus(serverEngine, serverResult); runDelegatedTasks(serverEngine); cTOs.compact(); sTOc.compact(); - /* - * After we've transfered all application data between the client - * and server, we close the clientEngine's outbound stream. - * This generates a close_notify handshake message, which the - * server engine receives and responds by closing itself. - */ + // After we've transferred all application data between the client + // and server, we close the clientEngine's outbound stream. + // This generates a close_notify handshake message, which the + // server engine receives and responds by closing itself. if (!dataDone && (clientOut.limit() == serverIn.position()) && (serverOut.limit() == clientIn.position())) { - /* - * A sanity check to ensure we got what was sent. - */ + // A sanity check to ensure we got what was sent. checkTransfer(serverOut, clientIn); checkTransfer(clientOut, serverIn); @@ -284,78 +197,33 @@ public class SSLEngineTemplate { } } + private static boolean isOpen(SSLEngine engine) { + return (!engine.isOutboundDone() || !engine.isInboundDone()); + } + private static void logEngineStatus(SSLEngine engine) { - log("\tCurrent HS State " + engine.getHandshakeStatus().toString()); - log("\tisInboundDone(): " + engine.isInboundDone()); + log("\tCurrent HS State: " + engine.getHandshakeStatus()); + log("\tisInboundDone() : " + engine.isInboundDone()); log("\tisOutboundDone(): " + engine.isOutboundDone()); } - /* - * Using the SSLContext created during object creation, - * create/configure the SSLEngines we'll use for this test. - */ - private void createSSLEngines() throws Exception { - /* - * Configure the serverEngine to act as a server in the SSL/TLS - * handshake. Also, require SSL client authentication. - */ - serverEngine = sslc.createSSLEngine(); - serverEngine.setUseClientMode(false); - serverEngine.setNeedClientAuth(true); - - // Get/set parameters if needed - SSLParameters paramsServer = serverEngine.getSSLParameters(); - serverEngine.setSSLParameters(paramsServer); - - /* - * Similar to above, but using client mode instead. - */ - clientEngine = sslc.createSSLEngine("client", 80); - clientEngine.setUseClientMode(true); - - // Get/set parameters if needed - SSLParameters paramsClient = clientEngine.getSSLParameters(); - clientEngine.setSSLParameters(paramsClient); + private static void logEngineStatus( + SSLEngine engine, SSLEngineResult result) { + log("\tResult Status : " + result.getStatus()); + log("\tResult HS Status : " + result.getHandshakeStatus()); + log("\tEngine HS Status : " + engine.getHandshakeStatus()); + log("\tisInboundDone() : " + engine.isInboundDone()); + log("\tisOutboundDone() : " + engine.isOutboundDone()); + log("\tMore Result : " + result); } - /* - * Create and size the buffers appropriately. - */ - private void createBuffers() { - - /* - * We'll assume the buffer sizes are the same - * between client and server. - */ - SSLSession session = clientEngine.getSession(); - int appBufferMax = session.getApplicationBufferSize(); - int netBufferMax = session.getPacketBufferSize(); - - /* - * We'll make the input buffers a bit bigger than the max needed - * size, so that unwrap()s following a successful data transfer - * won't generate BUFFER_OVERFLOWS. - * - * We'll use a mix of direct and indirect ByteBuffers for - * tutorial purposes only. In reality, only use direct - * ByteBuffers when they give a clear performance enhancement. - */ - clientIn = ByteBuffer.allocate(appBufferMax + 50); - serverIn = ByteBuffer.allocate(appBufferMax + 50); - - cTOs = ByteBuffer.allocateDirect(netBufferMax); - sTOc = ByteBuffer.allocateDirect(netBufferMax); - - clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes()); - serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes()); + private static void log(String message) { + System.err.println(message); } - /* - * If the result indicates that we have outstanding tasks to do, - * go ahead and run them in this thread. - */ + // If the result indicates that we have outstanding tasks to do, + // go ahead and run them in this thread. private static void runDelegatedTasks(SSLEngine engine) throws Exception { - if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { Runnable runnable; while ((runnable = engine.getDelegatedTask()) != null) { @@ -365,19 +233,13 @@ public class SSLEngineTemplate { HandshakeStatus hsStatus = engine.getHandshakeStatus(); if (hsStatus == HandshakeStatus.NEED_TASK) { throw new Exception( - "handshake shouldn't need additional tasks"); + "handshake shouldn't need additional tasks"); } logEngineStatus(engine); } } - private static boolean isEngineClosed(SSLEngine engine) { - return (engine.isOutboundDone() && engine.isInboundDone()); - } - - /* - * Simple check to make sure everything came across as expected. - */ + // Simple check to make sure everything came across as expected. private static void checkTransfer(ByteBuffer a, ByteBuffer b) throws Exception { a.flip(); @@ -394,35 +256,4 @@ public class SSLEngineTemplate { a.limit(a.capacity()); b.limit(b.capacity()); } - - /* - * Logging code - */ - private static boolean resultOnce = true; - - private static void log(String str, SSLEngineResult result) { - if (!logging) { - return; - } - if (resultOnce) { - resultOnce = false; - System.out.println("The format of the SSLEngineResult is: \n" + - "\t\"getStatus() / getHandshakeStatus()\" +\n" + - "\t\"bytesConsumed() / bytesProduced()\"\n"); - } - HandshakeStatus hsStatus = result.getHandshakeStatus(); - log(str + - result.getStatus() + "/" + hsStatus + ", " + - result.bytesConsumed() + "/" + result.bytesProduced() + - " bytes"); - if (hsStatus == HandshakeStatus.FINISHED) { - log("\t...ready for application data"); - } - } - - private static void log(String str) { - if (logging) { - System.out.println(str); - } - } }