| 1 | /* |
| 2 | * Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved. |
| 3 | * |
| 4 | * This code is free software; you can redistribute it and/or modify it |
| 5 | * under the terms of the GNU General Public License version 2 only, as |
| 6 | * published by the Free Software Foundation. |
| 7 | * |
| 8 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 11 | * version 2 for more details (a copy is included in the LICENSE file that |
| 12 | * accompanied this code). |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License version |
| 15 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 17 | * |
| 18 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| 19 | * or visit www.oracle.com if you need additional information or have any |
| 20 | * questions. |
| 21 | * |
| 22 | */ |
| 23 | |
| 24 | #include "precompiled.hpp" |
| 25 | #include "code/codeCache.hpp" |
| 26 | #include "code/nmethod.hpp" |
| 27 | #include "gc/shenandoah/shenandoahHeap.inline.hpp" |
| 28 | #include "gc/shenandoah/shenandoahCodeRoots.hpp" |
| 29 | #include "gc/shenandoah/shenandoahUtils.hpp" |
| 30 | #include "memory/resourceArea.hpp" |
| 31 | #include "memory/universe.hpp" |
| 32 | |
| 33 | ShenandoahParallelCodeCacheIterator::ShenandoahParallelCodeCacheIterator(const GrowableArray<CodeHeap*>* heaps) { |
| 34 | _length = heaps->length(); |
| 35 | _iters = NEW_C_HEAP_ARRAY(ShenandoahParallelCodeHeapIterator, _length, mtGC); |
| 36 | for (int h = 0; h < _length; h++) { |
| 37 | _iters[h] = ShenandoahParallelCodeHeapIterator(heaps->at(h)); |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | ShenandoahParallelCodeCacheIterator::~ShenandoahParallelCodeCacheIterator() { |
| 42 | FREE_C_HEAP_ARRAY(ParallelCodeHeapIterator, _iters); |
| 43 | } |
| 44 | |
| 45 | void ShenandoahParallelCodeCacheIterator::parallel_blobs_do(CodeBlobClosure* f) { |
| 46 | for (int c = 0; c < _length; c++) { |
| 47 | _iters[c].parallel_blobs_do(f); |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | ShenandoahParallelCodeHeapIterator::ShenandoahParallelCodeHeapIterator(CodeHeap* heap) : |
| 52 | _heap(heap), _claimed_idx(0), _finished(false) { |
| 53 | } |
| 54 | |
| 55 | void ShenandoahParallelCodeHeapIterator::parallel_blobs_do(CodeBlobClosure* f) { |
| 56 | assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint" ); |
| 57 | |
| 58 | /* |
| 59 | * Parallel code heap walk. |
| 60 | * |
| 61 | * This code makes all threads scan all code heaps, but only one thread would execute the |
| 62 | * closure on given blob. This is achieved by recording the "claimed" blocks: if a thread |
| 63 | * had claimed the block, it can process all blobs in it. Others have to fast-forward to |
| 64 | * next attempt without processing. |
| 65 | * |
| 66 | * Late threads would return immediately if iterator is finished. |
| 67 | */ |
| 68 | |
| 69 | if (_finished) { |
| 70 | return; |
| 71 | } |
| 72 | |
| 73 | int stride = 256; // educated guess |
| 74 | int stride_mask = stride - 1; |
| 75 | assert (is_power_of_2(stride), "sanity" ); |
| 76 | |
| 77 | int count = 0; |
| 78 | bool process_block = true; |
| 79 | |
| 80 | for (CodeBlob *cb = CodeCache::first_blob(_heap); cb != NULL; cb = CodeCache::next_blob(_heap, cb)) { |
| 81 | int current = count++; |
| 82 | if ((current & stride_mask) == 0) { |
| 83 | process_block = (current >= _claimed_idx) && |
| 84 | (Atomic::cmpxchg(current + stride, &_claimed_idx, current) == current); |
| 85 | } |
| 86 | if (process_block) { |
| 87 | if (cb->is_alive()) { |
| 88 | f->do_code_blob(cb); |
| 89 | #ifdef ASSERT |
| 90 | if (cb->is_nmethod()) |
| 91 | Universe::heap()->verify_nmethod((nmethod*)cb); |
| 92 | #endif |
| 93 | } |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | _finished = true; |
| 98 | } |
| 99 | |
| 100 | class ShenandoahNMethodOopDetector : public OopClosure { |
| 101 | private: |
| 102 | ResourceMark rm; // For growable array allocation below. |
| 103 | GrowableArray<oop*> _oops; |
| 104 | |
| 105 | public: |
| 106 | ShenandoahNMethodOopDetector() : _oops(10) {}; |
| 107 | |
| 108 | void do_oop(oop* o) { |
| 109 | _oops.append(o); |
| 110 | } |
| 111 | void do_oop(narrowOop* o) { |
| 112 | fatal("NMethods should not have compressed oops embedded." ); |
| 113 | } |
| 114 | |
| 115 | GrowableArray<oop*>* oops() { |
| 116 | return &_oops; |
| 117 | } |
| 118 | |
| 119 | bool has_oops() { |
| 120 | return !_oops.is_empty(); |
| 121 | } |
| 122 | }; |
| 123 | |
| 124 | GrowableArray<ShenandoahNMethod*>* ShenandoahCodeRoots::_recorded_nms; |
| 125 | ShenandoahLock ShenandoahCodeRoots::_recorded_nms_lock; |
| 126 | |
| 127 | void ShenandoahCodeRoots::initialize() { |
| 128 | _recorded_nms = new (ResourceObj::C_HEAP, mtGC) GrowableArray<ShenandoahNMethod*>(100, true, mtGC); |
| 129 | } |
| 130 | |
| 131 | void ShenandoahCodeRoots::add_nmethod(nmethod* nm) { |
| 132 | switch (ShenandoahCodeRootsStyle) { |
| 133 | case 0: |
| 134 | case 1: |
| 135 | break; |
| 136 | case 2: { |
| 137 | assert_locked_or_safepoint(CodeCache_lock); |
| 138 | ShenandoahLocker locker(CodeCache_lock->owned_by_self() ? NULL : &_recorded_nms_lock); |
| 139 | |
| 140 | ShenandoahNMethodOopDetector detector; |
| 141 | nm->oops_do(&detector); |
| 142 | |
| 143 | if (detector.has_oops()) { |
| 144 | ShenandoahNMethod* nmr = new ShenandoahNMethod(nm, detector.oops()); |
| 145 | nmr->assert_alive_and_correct(); |
| 146 | int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod); |
| 147 | if (idx != -1) { |
| 148 | ShenandoahNMethod* old = _recorded_nms->at(idx); |
| 149 | _recorded_nms->at_put(idx, nmr); |
| 150 | delete old; |
| 151 | } else { |
| 152 | _recorded_nms->append(nmr); |
| 153 | } |
| 154 | } |
| 155 | break; |
| 156 | } |
| 157 | default: |
| 158 | ShouldNotReachHere(); |
| 159 | } |
| 160 | }; |
| 161 | |
| 162 | void ShenandoahCodeRoots::remove_nmethod(nmethod* nm) { |
| 163 | switch (ShenandoahCodeRootsStyle) { |
| 164 | case 0: |
| 165 | case 1: { |
| 166 | break; |
| 167 | } |
| 168 | case 2: { |
| 169 | assert_locked_or_safepoint(CodeCache_lock); |
| 170 | ShenandoahLocker locker(CodeCache_lock->owned_by_self() ? NULL : &_recorded_nms_lock); |
| 171 | |
| 172 | ShenandoahNMethodOopDetector detector; |
| 173 | nm->oops_do(&detector, /* allow_dead = */ true); |
| 174 | |
| 175 | if (detector.has_oops()) { |
| 176 | int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod); |
| 177 | assert(idx != -1, "nmethod " PTR_FORMAT " should be registered" , p2i(nm)); |
| 178 | ShenandoahNMethod* old = _recorded_nms->at(idx); |
| 179 | old->assert_same_oops(detector.oops()); |
| 180 | _recorded_nms->delete_at(idx); |
| 181 | delete old; |
| 182 | } |
| 183 | break; |
| 184 | } |
| 185 | default: |
| 186 | ShouldNotReachHere(); |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | ShenandoahCodeRootsIterator::ShenandoahCodeRootsIterator() : |
| 191 | _heap(ShenandoahHeap::heap()), |
| 192 | _par_iterator(CodeCache::heaps()), |
| 193 | _claimed(0) { |
| 194 | assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint" ); |
| 195 | assert(!Thread::current()->is_Worker_thread(), "Should not be acquired by workers" ); |
| 196 | switch (ShenandoahCodeRootsStyle) { |
| 197 | case 0: |
| 198 | case 1: { |
| 199 | // No need to do anything here |
| 200 | break; |
| 201 | } |
| 202 | case 2: { |
| 203 | CodeCache_lock->lock(); |
| 204 | break; |
| 205 | } |
| 206 | default: |
| 207 | ShouldNotReachHere(); |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | ShenandoahCodeRootsIterator::~ShenandoahCodeRootsIterator() { |
| 212 | switch (ShenandoahCodeRootsStyle) { |
| 213 | case 0: |
| 214 | case 1: { |
| 215 | // No need to do anything here |
| 216 | break; |
| 217 | } |
| 218 | case 2: { |
| 219 | CodeCache_lock->unlock(); |
| 220 | break; |
| 221 | } |
| 222 | default: |
| 223 | ShouldNotReachHere(); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | template<bool CSET_FILTER> |
| 228 | void ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do(CodeBlobClosure *f) { |
| 229 | switch (ShenandoahCodeRootsStyle) { |
| 230 | case 0: { |
| 231 | if (_seq_claimed.try_set()) { |
| 232 | CodeCache::blobs_do(f); |
| 233 | } |
| 234 | break; |
| 235 | } |
| 236 | case 1: { |
| 237 | _par_iterator.parallel_blobs_do(f); |
| 238 | break; |
| 239 | } |
| 240 | case 2: { |
| 241 | ShenandoahCodeRootsIterator::fast_parallel_blobs_do<CSET_FILTER>(f); |
| 242 | break; |
| 243 | } |
| 244 | default: |
| 245 | ShouldNotReachHere(); |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | void ShenandoahAllCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) { |
| 250 | ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do<false>(f); |
| 251 | } |
| 252 | |
| 253 | void ShenandoahCsetCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) { |
| 254 | ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do<true>(f); |
| 255 | } |
| 256 | |
| 257 | template <bool CSET_FILTER> |
| 258 | void ShenandoahCodeRootsIterator::fast_parallel_blobs_do(CodeBlobClosure *f) { |
| 259 | assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint" ); |
| 260 | |
| 261 | size_t stride = 256; // educated guess |
| 262 | |
| 263 | GrowableArray<ShenandoahNMethod*>* list = ShenandoahCodeRoots::_recorded_nms; |
| 264 | |
| 265 | size_t max = (size_t)list->length(); |
| 266 | while (_claimed < max) { |
| 267 | size_t cur = Atomic::add(stride, &_claimed) - stride; |
| 268 | size_t start = cur; |
| 269 | size_t end = MIN2(cur + stride, max); |
| 270 | if (start >= max) break; |
| 271 | |
| 272 | for (size_t idx = start; idx < end; idx++) { |
| 273 | ShenandoahNMethod* nmr = list->at((int) idx); |
| 274 | nmr->assert_alive_and_correct(); |
| 275 | |
| 276 | if (CSET_FILTER && !nmr->has_cset_oops(_heap)) { |
| 277 | continue; |
| 278 | } |
| 279 | |
| 280 | f->do_code_blob(nmr->nm()); |
| 281 | } |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray<oop*>* oops) { |
| 286 | _nm = nm; |
| 287 | _oops = NEW_C_HEAP_ARRAY(oop*, oops->length(), mtGC); |
| 288 | _oops_count = oops->length(); |
| 289 | for (int c = 0; c < _oops_count; c++) { |
| 290 | _oops[c] = oops->at(c); |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | ShenandoahNMethod::~ShenandoahNMethod() { |
| 295 | if (_oops != NULL) { |
| 296 | FREE_C_HEAP_ARRAY(oop*, _oops); |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | bool ShenandoahNMethod::has_cset_oops(ShenandoahHeap *heap) { |
| 301 | for (int c = 0; c < _oops_count; c++) { |
| 302 | oop o = RawAccess<>::oop_load(_oops[c]); |
| 303 | if (heap->in_collection_set(o)) { |
| 304 | return true; |
| 305 | } |
| 306 | } |
| 307 | return false; |
| 308 | } |
| 309 | |
| 310 | #ifdef ASSERT |
| 311 | void ShenandoahNMethod::assert_alive_and_correct() { |
| 312 | assert(_nm->is_alive(), "only alive nmethods here" ); |
| 313 | assert(_oops_count > 0, "should have filtered nmethods without oops before" ); |
| 314 | ShenandoahHeap* heap = ShenandoahHeap::heap(); |
| 315 | for (int c = 0; c < _oops_count; c++) { |
| 316 | oop *loc = _oops[c]; |
| 317 | assert(_nm->code_contains((address) loc) || _nm->oops_contains(loc), "nmethod should contain the oop*" ); |
| 318 | oop o = RawAccess<>::oop_load(loc); |
| 319 | shenandoah_assert_correct_except(loc, o, o == NULL || heap->is_full_gc_move_in_progress()); |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | void ShenandoahNMethod::assert_same_oops(GrowableArray<oop*>* oops) { |
| 324 | assert(_oops_count == oops->length(), "should have the same number of oop*" ); |
| 325 | for (int c = 0; c < _oops_count; c++) { |
| 326 | assert(_oops[c] == oops->at(c), "should be the same oop*" ); |
| 327 | } |
| 328 | } |
| 329 | #endif |
| 330 | |