/* * Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved. * * 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 "code/codeCache.hpp" #include "code/nmethod.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahCodeRoots.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "runtime/atomic.hpp" ShenandoahParallelCodeCacheIterator::ShenandoahParallelCodeCacheIterator(const GrowableArray* heaps) { _length = heaps->length(); _iters = NEW_C_HEAP_ARRAY(ShenandoahParallelCodeHeapIterator, _length, mtGC); for (int h = 0; h < _length; h++) { _iters[h] = ShenandoahParallelCodeHeapIterator(heaps->at(h)); } } ShenandoahParallelCodeCacheIterator::~ShenandoahParallelCodeCacheIterator() { FREE_C_HEAP_ARRAY(ParallelCodeHeapIterator, _iters); } void ShenandoahParallelCodeCacheIterator::parallel_blobs_do(CodeBlobClosure* f) { for (int c = 0; c < _length; c++) { _iters[c].parallel_blobs_do(f); } } ShenandoahParallelCodeHeapIterator::ShenandoahParallelCodeHeapIterator(CodeHeap* heap) : _heap(heap), _claimed_idx(0), _finished(false) { } void ShenandoahParallelCodeHeapIterator::parallel_blobs_do(CodeBlobClosure* f) { assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); /* * Parallel code heap walk. * * This code makes all threads scan all code heaps, but only one thread would execute the * closure on given blob. This is achieved by recording the "claimed" blocks: if a thread * had claimed the block, it can process all blobs in it. Others have to fast-forward to * next attempt without processing. * * Late threads would return immediately if iterator is finished. */ if (_finished) { return; } int stride = 256; // educated guess int stride_mask = stride - 1; assert (is_power_of_2(stride), "sanity"); int count = 0; bool process_block = true; for (CodeBlob *cb = CodeCache::first_blob(_heap); cb != NULL; cb = CodeCache::next_blob(_heap, cb)) { int current = count++; if ((current & stride_mask) == 0) { process_block = (current >= _claimed_idx) && (Atomic::cmpxchg(&_claimed_idx, current, current + stride) == current); } if (process_block) { if (cb->is_alive()) { f->do_code_blob(cb); #ifdef ASSERT if (cb->is_nmethod()) Universe::heap()->verify_nmethod((nmethod*)cb); #endif } } } _finished = true; } class ShenandoahNMethodOopDetector : public OopClosure { private: ResourceMark rm; // For growable array allocation below. GrowableArray _oops; public: ShenandoahNMethodOopDetector() : _oops(10) {}; void do_oop(oop* o) { _oops.append(o); } void do_oop(narrowOop* o) { fatal("NMethods should not have compressed oops embedded."); } GrowableArray* oops() { return &_oops; } bool has_oops() { return !_oops.is_empty(); } }; GrowableArray* ShenandoahCodeRoots::_recorded_nms; ShenandoahLock ShenandoahCodeRoots::_recorded_nms_lock; void ShenandoahCodeRoots::initialize() { _recorded_nms = new (ResourceObj::C_HEAP, mtGC) GrowableArray(100, true, mtGC); } void ShenandoahCodeRoots::add_nmethod(nmethod* nm) { switch (ShenandoahCodeRootsStyle) { case 0: case 1: break; case 2: { assert_locked_or_safepoint(CodeCache_lock); ShenandoahLocker locker(CodeCache_lock->owned_by_self() ? NULL : &_recorded_nms_lock); ShenandoahNMethodOopDetector detector; nm->oops_do(&detector); if (detector.has_oops()) { ShenandoahNMethod* nmr = new ShenandoahNMethod(nm, detector.oops()); nmr->assert_alive_and_correct(); int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod); if (idx != -1) { ShenandoahNMethod* old = _recorded_nms->at(idx); _recorded_nms->at_put(idx, nmr); delete old; } else { _recorded_nms->append(nmr); } } break; } default: ShouldNotReachHere(); } }; void ShenandoahCodeRoots::remove_nmethod(nmethod* nm) { switch (ShenandoahCodeRootsStyle) { case 0: case 1: { break; } case 2: { assert_locked_or_safepoint(CodeCache_lock); ShenandoahLocker locker(CodeCache_lock->owned_by_self() ? NULL : &_recorded_nms_lock); ShenandoahNMethodOopDetector detector; nm->oops_do(&detector, /* allow_dead = */ true); if (detector.has_oops()) { int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod); assert(idx != -1, "nmethod " PTR_FORMAT " should be registered", p2i(nm)); ShenandoahNMethod* old = _recorded_nms->at(idx); old->assert_same_oops(detector.oops()); _recorded_nms->delete_at(idx); delete old; } break; } default: ShouldNotReachHere(); } } ShenandoahCodeRootsIterator::ShenandoahCodeRootsIterator() : _heap(ShenandoahHeap::heap()), _par_iterator(CodeCache::heaps()), _claimed(0) { assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); assert(!Thread::current()->is_Worker_thread(), "Should not be acquired by workers"); switch (ShenandoahCodeRootsStyle) { case 0: case 1: { // No need to do anything here break; } case 2: { CodeCache_lock->lock_without_safepoint_check(); break; } default: ShouldNotReachHere(); } } ShenandoahCodeRootsIterator::~ShenandoahCodeRootsIterator() { switch (ShenandoahCodeRootsStyle) { case 0: case 1: { // No need to do anything here break; } case 2: { CodeCache_lock->unlock(); break; } default: ShouldNotReachHere(); } } template void ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do(CodeBlobClosure *f) { switch (ShenandoahCodeRootsStyle) { case 0: { if (_seq_claimed.try_set()) { CodeCache::blobs_do(f); } break; } case 1: { _par_iterator.parallel_blobs_do(f); break; } case 2: { ShenandoahCodeRootsIterator::fast_parallel_blobs_do(f); break; } default: ShouldNotReachHere(); } } void ShenandoahAllCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) { ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do(f); } void ShenandoahCsetCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) { ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do(f); } template void ShenandoahCodeRootsIterator::fast_parallel_blobs_do(CodeBlobClosure *f) { assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); size_t stride = 256; // educated guess GrowableArray* list = ShenandoahCodeRoots::_recorded_nms; size_t max = (size_t)list->length(); while (_claimed < max) { size_t cur = Atomic::add(&_claimed, stride) - stride; size_t start = cur; size_t end = MIN2(cur + stride, max); if (start >= max) break; for (size_t idx = start; idx < end; idx++) { ShenandoahNMethod* nmr = list->at((int) idx); nmr->assert_alive_and_correct(); if (CSET_FILTER && !nmr->has_cset_oops(_heap)) { continue; } f->do_code_blob(nmr->nm()); } } } ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray* oops) { _nm = nm; _oops = NEW_C_HEAP_ARRAY(oop*, oops->length(), mtGC); _oops_count = oops->length(); for (int c = 0; c < _oops_count; c++) { _oops[c] = oops->at(c); } } ShenandoahNMethod::~ShenandoahNMethod() { if (_oops != NULL) { FREE_C_HEAP_ARRAY(oop*, _oops); } } bool ShenandoahNMethod::has_cset_oops(ShenandoahHeap *heap) { for (int c = 0; c < _oops_count; c++) { oop o = RawAccess<>::oop_load(_oops[c]); if (heap->in_collection_set(o)) { return true; } } return false; } #ifdef ASSERT void ShenandoahNMethod::assert_alive_and_correct() { assert(_nm->is_alive(), "only alive nmethods here"); assert(_oops_count > 0, "should have filtered nmethods without oops before"); ShenandoahHeap* heap = ShenandoahHeap::heap(); for (int c = 0; c < _oops_count; c++) { oop *loc = _oops[c]; assert(_nm->code_contains((address) loc) || _nm->oops_contains(loc), "nmethod should contain the oop*"); oop o = RawAccess<>::oop_load(loc); shenandoah_assert_correct_except(loc, o, o == NULL || heap->is_full_gc_move_in_progress() || (VMThread::vm_operation() != NULL) && (VMThread::vm_operation()->type() == VM_Operation::VMOp_HeapWalkOperation) ); } } void ShenandoahNMethod::assert_same_oops(GrowableArray* oops) { assert(_oops_count == oops->length(), "should have the same number of oop*"); for (int c = 0; c < _oops_count; c++) { assert(_oops[c] == oops->at(c), "should be the same oop*"); } } #endif