1 /* 2 * Copyright (c) 2017, 2018, 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.hpp" 28 #include "gc/shenandoah/shenandoahHeap.inline.hpp" 29 #include "gc/shenandoah/shenandoahCodeRoots.hpp" 30 #include "memory/resourceArea.hpp" 31 32 ShenandoahParallelCodeCacheIterator::ShenandoahParallelCodeCacheIterator(const GrowableArray<CodeHeap*>* heaps) { 33 _length = heaps->length(); 34 _iters = NEW_C_HEAP_ARRAY(ShenandoahParallelCodeHeapIterator, _length, mtGC); 35 for (int h = 0; h < _length; h++) { 36 _iters[h] = ShenandoahParallelCodeHeapIterator(heaps->at(h)); 37 } 38 } 39 40 ShenandoahParallelCodeCacheIterator::~ShenandoahParallelCodeCacheIterator() { 41 FREE_C_HEAP_ARRAY(ParallelCodeHeapIterator, _iters); 42 } 43 44 void ShenandoahParallelCodeCacheIterator::parallel_blobs_do(CodeBlobClosure* f) { 45 for (int c = 0; c < _length; c++) { 46 _iters[c].parallel_blobs_do(f); 47 } 48 } 49 50 ShenandoahParallelCodeHeapIterator::ShenandoahParallelCodeHeapIterator(CodeHeap* heap) : 51 _heap(heap), _claimed_idx(0), _finished(false) { 52 } 53 54 void ShenandoahParallelCodeHeapIterator::parallel_blobs_do(CodeBlobClosure* f) { 55 assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); 56 57 /* 58 * Parallel code heap walk. 59 * 60 * This code makes all threads scan all code heaps, but only one thread would execute the 61 * closure on given blob. This is achieved by recording the "claimed" blocks: if a thread 62 * had claimed the block, it can process all blobs in it. Others have to fast-forward to 63 * next attempt without processing. 64 * 65 * Late threads would return immediately if iterator is finished. 66 */ 67 68 if (_finished) { 69 return; 70 } 71 72 int stride = 256; // educated guess 73 int stride_mask = stride - 1; 74 assert (is_power_of_2(stride), "sanity"); 75 76 int count = 0; 77 bool process_block = true; 78 79 for (CodeBlob *cb = CodeCache::first_blob(_heap); cb != NULL; cb = CodeCache::next_blob(_heap, cb)) { 80 int current = count++; 81 if ((current & stride_mask) == 0) { 82 process_block = (current >= _claimed_idx) && 83 (Atomic::cmpxchg(current + stride, &_claimed_idx, current) == current); 84 } 85 if (process_block) { 86 if (cb->is_alive()) { 87 f->do_code_blob(cb); 88 #ifdef ASSERT 89 if (cb->is_nmethod()) 90 Universe::heap()->verify_nmethod((nmethod*)cb); 91 #endif 92 } 93 } 94 } 95 96 _finished = true; 97 } 98 99 class ShenandoahNMethodOopDetector : public OopClosure { 100 private: 101 ResourceMark rm; // For growable array allocation below. 102 GrowableArray<oop*> _oops; 103 104 public: 105 ShenandoahNMethodOopDetector() : _oops(10) {}; 106 107 void do_oop(oop* o) { 108 _oops.append(o); 109 } 110 void do_oop(narrowOop* o) { 111 fatal("NMethods should not have compressed oops embedded."); 112 } 113 114 GrowableArray<oop*>* oops() { 115 return &_oops; 116 } 117 118 bool has_oops() { 119 return !_oops.is_empty(); 120 } 121 }; 122 123 class ShenandoahNMethodOopInitializer : public OopClosure { 124 private: 125 ShenandoahHeap* const _heap; 126 127 public: 128 ShenandoahNMethodOopInitializer() : _heap(ShenandoahHeap::heap()) {}; 129 130 private: 131 template <class T> 132 inline void do_oop_work(T* p) { 133 T o = RawAccess<>::oop_load(p); 134 if (! CompressedOops::is_null(o)) { 135 oop obj1 = CompressedOops::decode_not_null(o); 136 oop obj2 = ShenandoahBarrierSet::barrier_set()->write_barrier(obj1); 137 if (! oopDesc::equals_raw(obj1, obj2)) { 138 shenandoah_assert_not_in_cset(NULL, obj2); 139 RawAccess<IS_NOT_NULL>::oop_store(p, obj2); 140 if (_heap->is_concurrent_traversal_in_progress()) { 141 ShenandoahBarrierSet::barrier_set()->enqueue(obj2); 142 } 143 } 144 } 145 } 146 147 public: 148 void do_oop(oop* o) { 149 do_oop_work(o); 150 } 151 void do_oop(narrowOop* o) { 152 do_oop_work(o); 153 } 154 }; 155 156 ShenandoahCodeRoots::PaddedLock ShenandoahCodeRoots::_recorded_nms_lock; 157 GrowableArray<ShenandoahNMethod*>* ShenandoahCodeRoots::_recorded_nms; 158 159 void ShenandoahCodeRoots::initialize() { 160 _recorded_nms_lock._lock = 0; 161 _recorded_nms = new (ResourceObj::C_HEAP, mtGC) GrowableArray<ShenandoahNMethod*>(100, true, mtGC); 162 } 163 164 void ShenandoahCodeRoots::add_nmethod(nmethod* nm) { 165 switch (ShenandoahCodeRootsStyle) { 166 case 0: 167 case 1: { 168 ShenandoahNMethodOopInitializer init; 169 nm->oops_do(&init); 170 nm->fix_oop_relocations(); 171 break; 172 } 173 case 2: { 174 ShenandoahNMethodOopDetector detector; 175 nm->oops_do(&detector); 176 177 if (detector.has_oops()) { 178 ShenandoahNMethodOopInitializer init; 179 nm->oops_do(&init); 180 nm->fix_oop_relocations(); 181 182 ShenandoahNMethod* nmr = new ShenandoahNMethod(nm, detector.oops()); 183 nmr->assert_alive_and_correct(); 184 185 ShenandoahCodeRootsLock lock(true); 186 187 int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod); 188 if (idx != -1) { 189 ShenandoahNMethod* old = _recorded_nms->at(idx); 190 _recorded_nms->at_put(idx, nmr); 191 delete old; 192 } else { 193 _recorded_nms->append(nmr); 194 } 195 } 196 break; 197 } 198 default: 199 ShouldNotReachHere(); 200 } 201 }; 202 203 void ShenandoahCodeRoots::remove_nmethod(nmethod* nm) { 204 switch (ShenandoahCodeRootsStyle) { 205 case 0: 206 case 1: { 207 break; 208 } 209 case 2: { 210 ShenandoahNMethodOopDetector detector; 211 nm->oops_do(&detector, /* allow_zombie = */ true); 212 213 if (detector.has_oops()) { 214 ShenandoahCodeRootsLock lock(true); 215 216 int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod); 217 assert(idx != -1, "nmethod " PTR_FORMAT " should be registered", p2i(nm)); 218 ShenandoahNMethod* old = _recorded_nms->at(idx); 219 old->assert_same_oops(detector.oops()); 220 _recorded_nms->delete_at(idx); 221 delete old; 222 } 223 break; 224 } 225 default: 226 ShouldNotReachHere(); 227 } 228 } 229 230 ShenandoahCodeRootsIterator::ShenandoahCodeRootsIterator() : 231 _heap(ShenandoahHeap::heap()), 232 _par_iterator(CodeCache::heaps()), 233 _claimed(0) { 234 assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); 235 assert(!Thread::current()->is_Worker_thread(), "Should not be acquired by workers"); 236 switch (ShenandoahCodeRootsStyle) { 237 case 0: 238 case 1: { 239 // No need to do anything here 240 break; 241 } 242 case 2: { 243 ShenandoahCodeRoots::acquire_lock(false); 244 break; 245 } 246 default: 247 ShouldNotReachHere(); 248 } 249 } 250 251 ShenandoahCodeRootsIterator::~ShenandoahCodeRootsIterator() { 252 switch (ShenandoahCodeRootsStyle) { 253 case 0: 254 case 1: { 255 // No need to do anything here 256 break; 257 } 258 case 2: { 259 ShenandoahCodeRoots::release_lock(false); 260 break; 261 } 262 default: 263 ShouldNotReachHere(); 264 } 265 } 266 267 template<bool CSET_FILTER> 268 void ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do(CodeBlobClosure *f) { 269 switch (ShenandoahCodeRootsStyle) { 270 case 0: { 271 if (_seq_claimed.try_set()) { 272 CodeCache::blobs_do(f); 273 } 274 break; 275 } 276 case 1: { 277 _par_iterator.parallel_blobs_do(f); 278 break; 279 } 280 case 2: { 281 ShenandoahCodeRootsIterator::fast_parallel_blobs_do<CSET_FILTER>(f); 282 break; 283 } 284 default: 285 ShouldNotReachHere(); 286 } 287 } 288 289 ShenandoahAllCodeRootsIterator ShenandoahCodeRoots::iterator() { 290 return ShenandoahAllCodeRootsIterator(); 291 } 292 293 ShenandoahCsetCodeRootsIterator ShenandoahCodeRoots::cset_iterator() { 294 return ShenandoahCsetCodeRootsIterator(); 295 } 296 297 void ShenandoahAllCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) { 298 ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do<false>(f); 299 } 300 301 void ShenandoahCsetCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) { 302 ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do<true>(f); 303 } 304 305 template <bool CSET_FILTER> 306 void ShenandoahCodeRootsIterator::fast_parallel_blobs_do(CodeBlobClosure *f) { 307 assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); 308 309 size_t stride = 256; // educated guess 310 311 GrowableArray<ShenandoahNMethod*>* list = ShenandoahCodeRoots::_recorded_nms; 312 313 size_t max = (size_t)list->length(); 314 while (_claimed < max) { 315 size_t cur = Atomic::add(stride, &_claimed) - stride; 316 size_t start = cur; 317 size_t end = MIN2(cur + stride, max); 318 if (start >= max) break; 319 320 for (size_t idx = start; idx < end; idx++) { 321 ShenandoahNMethod* nmr = list->at((int) idx); 322 nmr->assert_alive_and_correct(); 323 324 if (CSET_FILTER && !nmr->has_cset_oops(_heap)) { 325 continue; 326 } 327 328 f->do_code_blob(nmr->nm()); 329 } 330 } 331 } 332 333 ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray<oop*>* oops) { 334 _nm = nm; 335 _oops = NEW_C_HEAP_ARRAY(oop*, oops->length(), mtGC); 336 _oops_count = oops->length(); 337 for (int c = 0; c < _oops_count; c++) { 338 _oops[c] = oops->at(c); 339 } 340 } 341 342 ShenandoahNMethod::~ShenandoahNMethod() { 343 if (_oops != NULL) { 344 FREE_C_HEAP_ARRAY(oop*, _oops); 345 } 346 } 347 348 bool ShenandoahNMethod::has_cset_oops(ShenandoahHeap *heap) { 349 for (int c = 0; c < _oops_count; c++) { 350 oop o = RawAccess<>::oop_load(_oops[c]); 351 if (heap->in_collection_set(o)) { 352 return true; 353 } 354 } 355 return false; 356 } 357 358 #ifdef ASSERT 359 void ShenandoahNMethod::assert_alive_and_correct() { 360 assert(_nm->is_alive(), "only alive nmethods here"); 361 assert(_oops_count > 0, "should have filtered nmethods without oops before"); 362 ShenandoahHeap* heap = ShenandoahHeap::heap(); 363 for (int c = 0; c < _oops_count; c++) { 364 oop *loc = _oops[c]; 365 assert(_nm->code_contains((address) loc) || _nm->oops_contains(loc), "nmethod should contain the oop*"); 366 oop o = RawAccess<>::oop_load(loc); 367 shenandoah_assert_correct_except(loc, o, o == NULL || heap->is_full_gc_move_in_progress()); 368 } 369 } 370 371 void ShenandoahNMethod::assert_same_oops(GrowableArray<oop*>* oops) { 372 assert(_oops_count == oops->length(), "should have the same number of oop*"); 373 for (int c = 0; c < _oops_count; c++) { 374 assert(_oops[c] == oops->at(c), "should be the same oop*"); 375 } 376 } 377 #endif