1 /* 2 * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "precompiled.hpp" 26 #include "code/nmethod.hpp" 27 #include "code/scopeDesc.hpp" 28 #include "gc/shared/barrierSet.hpp" 29 #include "gc/shared/barrierSetStackChunk.hpp" 30 #include "logging/log.hpp" 31 #include "logging/logStream.hpp" 32 #include "memory/memRegion.hpp" 33 #include "oops/instanceStackChunkKlass.inline.hpp" 34 #include "oops/oop.inline.hpp" 35 #include "oops/stackChunkOop.inline.hpp" 36 #include "runtime/frame.hpp" 37 #include "runtime/registerMap.hpp" 38 #include "runtime/smallRegisterMap.inline.hpp" 39 #include "runtime/stackChunkFrameStream.inline.hpp" 40 41 // Note: Some functions in this file work with stale object pointers, e.g. 42 // DerivedPointerSupport. Be extra careful to not put those pointers into 43 // variables of the 'oop' type. There's extra GC verification around oops 44 // that may fail when stale oops are being used. 45 46 template <typename RegisterMapT> 47 class FrameOopIterator : public OopIterator { 48 private: 49 const frame& _f; 50 const RegisterMapT* _map; 51 52 public: 53 FrameOopIterator(const frame& f, const RegisterMapT* map) 54 : _f(f), 55 _map(map) { 56 } 57 58 virtual void oops_do(OopClosure* cl) override { 59 if (_f.is_interpreted_frame()) { 60 _f.oops_interpreted_do(cl, nullptr); 61 } else { 62 OopMapDo<OopClosure, DerivedOopClosure, IncludeAllValues> visitor(cl, nullptr); 63 visitor.oops_do(&_f, _map, _f.oop_map()); 64 } 65 } 66 }; 67 68 class LockStackOopIterator : public OopIterator { 69 private: 70 const stackChunkOop _chunk; 71 public: 72 LockStackOopIterator(const stackChunkOop chunk) : _chunk(chunk) {} 73 74 virtual void oops_do(OopClosure* cl) override { 75 int cnt = _chunk->lockstack_size(); 76 oop* lockstack_start = (oop*)_chunk->start_address(); 77 for (int i = 0; i < cnt; i++) { 78 cl->do_oop(&lockstack_start[i]); 79 } 80 } 81 }; 82 83 frame stackChunkOopDesc::top_frame(RegisterMap* map) { 84 assert(!is_empty(), ""); 85 StackChunkFrameStream<ChunkFrames::Mixed> fs(this); 86 87 map->set_stack_chunk(this); 88 fs.initialize_register_map(map); 89 90 frame f = fs.to_frame(); 91 92 assert(to_offset(f.sp()) == sp(), "f.offset_sp(): %d sp(): %d async: %d", f.offset_sp(), sp(), map->is_async()); 93 relativize_frame(f); 94 f.set_frame_index(0); 95 return f; 96 } 97 98 frame stackChunkOopDesc::sender(const frame& f, RegisterMap* map) { 99 assert(map->in_cont(), ""); 100 assert(!map->include_argument_oops(), ""); 101 assert(!f.is_empty(), ""); 102 assert(map->stack_chunk() == this, ""); 103 assert(!is_empty(), ""); 104 105 int index = f.frame_index(); // we need to capture the index before calling derelativize, which destroys it 106 StackChunkFrameStream<ChunkFrames::Mixed> fs(this, derelativize(f)); 107 fs.next(map); 108 109 if (!fs.is_done()) { 110 frame sender = fs.to_frame(); 111 assert(is_usable_in_chunk(sender.unextended_sp()), ""); 112 relativize_frame(sender); 113 114 sender.set_frame_index(index+1); 115 return sender; 116 } 117 118 if (parent() != nullptr) { 119 assert(!parent()->is_empty(), ""); 120 return parent()->top_frame(map); 121 } 122 123 return Continuation::continuation_parent_frame(map); 124 } 125 126 static int num_java_frames(nmethod* nm, address pc) { 127 int count = 0; 128 for (ScopeDesc* scope = nm->scope_desc_at(pc); scope != nullptr; scope = scope->sender()) { 129 count++; 130 } 131 return count; 132 } 133 134 static int num_java_frames(const StackChunkFrameStream<ChunkFrames::Mixed>& f) { 135 assert(f.is_interpreted() 136 || (f.cb() != nullptr && f.cb()->is_nmethod() && f.cb()->as_nmethod()->is_java_method()), ""); 137 return f.is_interpreted() ? 1 : num_java_frames(f.cb()->as_nmethod(), f.orig_pc()); 138 } 139 140 int stackChunkOopDesc::num_java_frames() const { 141 int n = 0; 142 for (StackChunkFrameStream<ChunkFrames::Mixed> f(const_cast<stackChunkOopDesc*>(this)); !f.is_done(); 143 f.next(SmallRegisterMap::instance())) { 144 if (!f.is_stub()) { 145 n += ::num_java_frames(f); 146 } 147 } 148 return n; 149 } 150 151 template <stackChunkOopDesc::BarrierType barrier> 152 class DoBarriersStackClosure { 153 const stackChunkOop _chunk; 154 155 public: 156 DoBarriersStackClosure(stackChunkOop chunk) : _chunk(chunk) {} 157 158 template <ChunkFrames frame_kind, typename RegisterMapT> 159 bool do_frame(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { 160 _chunk->do_barriers0<barrier>(f, map); 161 return true; 162 } 163 }; 164 165 template <stackChunkOopDesc::BarrierType barrier> 166 void stackChunkOopDesc::do_barriers() { 167 DoBarriersStackClosure<barrier> closure(this); 168 iterate_stack(&closure); 169 } 170 171 template void stackChunkOopDesc::do_barriers<stackChunkOopDesc::BarrierType::Load> (); 172 template void stackChunkOopDesc::do_barriers<stackChunkOopDesc::BarrierType::Store>(); 173 174 class DerivedPointersSupport { 175 public: 176 static void relativize(derived_base* base_loc, derived_pointer* derived_loc) { 177 // The base oop could be stale from the GC's point-of-view. Treat it as an 178 // uintptr_t to stay clear of the oop verification code in oopsHierarcy.hpp. 179 uintptr_t base = *(uintptr_t*)base_loc; 180 if (base == 0) { 181 return; 182 } 183 assert(!UseCompressedOops || !CompressedOops::is_base((void*)base), ""); 184 185 // This is always a full derived pointer 186 uintptr_t derived_int_val = *(uintptr_t*)derived_loc; 187 188 // Make the pointer an offset (relativize) and store it at the same location 189 uintptr_t offset = derived_int_val - base; 190 *(uintptr_t*)derived_loc = offset; 191 } 192 193 static void derelativize(derived_base* base_loc, derived_pointer* derived_loc) { 194 uintptr_t base = *(uintptr_t*)base_loc; 195 if (base == 0) { 196 return; 197 } 198 assert(!UseCompressedOops || !CompressedOops::is_base((void*)base), ""); 199 200 // All derived pointers should have been relativized into offsets 201 uintptr_t offset = *(uintptr_t*)derived_loc; 202 203 // Restore the original derived pointer 204 *(uintptr_t*)derived_loc = base + offset; 205 } 206 207 struct RelativizeClosure : public DerivedOopClosure { 208 virtual void do_derived_oop(derived_base* base_loc, derived_pointer* derived_loc) override { 209 DerivedPointersSupport::relativize(base_loc, derived_loc); 210 } 211 }; 212 213 struct DerelativizeClosure : public DerivedOopClosure { 214 virtual void do_derived_oop(derived_base* base_loc, derived_pointer* derived_loc) override { 215 DerivedPointersSupport::derelativize(base_loc, derived_loc); 216 } 217 }; 218 }; 219 220 template <typename DerivedPointerClosureType> 221 class EncodeGCModeConcurrentFrameClosure { 222 stackChunkOop _chunk; 223 DerivedPointerClosureType* _cl; 224 225 public: 226 EncodeGCModeConcurrentFrameClosure(stackChunkOop chunk, DerivedPointerClosureType* cl) 227 : _chunk(chunk), 228 _cl(cl) { 229 } 230 231 template <ChunkFrames frame_kind, typename RegisterMapT> 232 bool do_frame(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { 233 f.iterate_derived_pointers(_cl, map); 234 235 BarrierSetStackChunk* bs_chunk = BarrierSet::barrier_set()->barrier_set_stack_chunk(); 236 frame fr = f.to_frame(); 237 FrameOopIterator<RegisterMapT> iterator(fr, map); 238 bs_chunk->encode_gc_mode(_chunk, &iterator); 239 240 return true; 241 } 242 243 bool do_lockstack() { 244 BarrierSetStackChunk* bs_chunk = BarrierSet::barrier_set()->barrier_set_stack_chunk(); 245 LockStackOopIterator iterator(_chunk); 246 bs_chunk->encode_gc_mode(_chunk, &iterator); 247 248 return true; 249 } 250 }; 251 252 bool stackChunkOopDesc::try_acquire_relativization() { 253 for (;;) { 254 // We use an acquiring load when reading the flags to ensure that if we leave this 255 // function thinking that relativization is finished, we know that if another thread 256 // did the relativization, we will still be able to observe the relativized derived 257 // pointers, which is important as subsequent modifications of derived pointers must 258 // happen after relativization. 259 uint8_t flags_before = flags_acquire(); 260 if ((flags_before & FLAG_GC_MODE) != 0) { 261 // Terminal state - relativization is ensured 262 return false; 263 } 264 265 if ((flags_before & FLAG_CLAIM_RELATIVIZE) != 0) { 266 // Someone else has claimed relativization - wait for completion 267 MonitorLocker ml(ContinuationRelativize_lock, Mutex::_no_safepoint_check_flag); 268 uint8_t flags_under_lock = flags_acquire(); 269 if ((flags_under_lock & FLAG_GC_MODE) != 0) { 270 // Terminal state - relativization is ensured 271 return false; 272 } 273 274 if ((flags_under_lock & FLAG_NOTIFY_RELATIVIZE) != 0) { 275 // Relativization is claimed by another thread, and it knows it needs to notify 276 ml.wait(); 277 } else if (try_set_flags(flags_under_lock, flags_under_lock | FLAG_NOTIFY_RELATIVIZE)) { 278 // Relativization is claimed by another thread, and it knows it needs to notify 279 ml.wait(); 280 } 281 // Retry - rerun the loop 282 continue; 283 } 284 285 if (try_set_flags(flags_before, flags_before | FLAG_CLAIM_RELATIVIZE)) { 286 // Claimed relativization - let's do it 287 return true; 288 } 289 } 290 } 291 292 void stackChunkOopDesc::release_relativization() { 293 for (;;) { 294 uint8_t flags_before = flags(); 295 if ((flags_before & FLAG_NOTIFY_RELATIVIZE) != 0) { 296 MonitorLocker ml(ContinuationRelativize_lock, Mutex::_no_safepoint_check_flag); 297 // No need to CAS the terminal state; nobody else can be racingly mutating here 298 // as both claim and notify flags are already set (and monotonic) 299 // We do however need to use a releasing store on the flags, to ensure that 300 // the reader of that value (using load_acquire) will be able to observe 301 // the relativization of the derived pointers 302 uint8_t flags_under_lock = flags(); 303 release_set_flags(flags_under_lock | FLAG_GC_MODE); 304 ml.notify_all(); 305 return; 306 } 307 308 if (try_set_flags(flags_before, flags_before | FLAG_GC_MODE)) { 309 // Successfully set the terminal state; we are done 310 return; 311 } 312 } 313 } 314 315 void stackChunkOopDesc::relativize_derived_pointers_concurrently() { 316 if (!try_acquire_relativization()) { 317 // Already relativized 318 return; 319 } 320 321 DerivedPointersSupport::RelativizeClosure derived_cl; 322 EncodeGCModeConcurrentFrameClosure<decltype(derived_cl)> frame_cl(this, &derived_cl); 323 iterate_stack(&frame_cl); 324 frame_cl.do_lockstack(); 325 326 release_relativization(); 327 } 328 329 class TransformStackChunkClosure { 330 stackChunkOop _chunk; 331 332 public: 333 TransformStackChunkClosure(stackChunkOop chunk) : _chunk(chunk) { } 334 335 template <ChunkFrames frame_kind, typename RegisterMapT> 336 bool do_frame(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { 337 DerivedPointersSupport::RelativizeClosure derived_cl; 338 f.iterate_derived_pointers(&derived_cl, map); 339 340 BarrierSetStackChunk* bs_chunk = BarrierSet::barrier_set()->barrier_set_stack_chunk(); 341 frame fr = f.to_frame(); 342 FrameOopIterator<RegisterMapT> iterator(fr, map); 343 bs_chunk->encode_gc_mode(_chunk, &iterator); 344 345 return true; 346 } 347 348 bool do_lockstack() { 349 BarrierSetStackChunk* bs_chunk = BarrierSet::barrier_set()->barrier_set_stack_chunk(); 350 LockStackOopIterator iterator(_chunk); 351 bs_chunk->encode_gc_mode(_chunk, &iterator); 352 353 return true; 354 } 355 }; 356 357 void stackChunkOopDesc::transform() { 358 assert(!is_gc_mode(), "Should only be called once per chunk"); 359 set_gc_mode(true); 360 361 assert(!has_bitmap(), "Should only be set once"); 362 set_has_bitmap(true); 363 bitmap().clear(); 364 365 TransformStackChunkClosure closure(this); 366 iterate_stack(&closure); 367 closure.do_lockstack(); 368 } 369 370 template <stackChunkOopDesc::BarrierType barrier, bool compressedOopsWithBitmap> 371 class BarrierClosure: public OopClosure { 372 NOT_PRODUCT(intptr_t* _sp;) 373 374 public: 375 BarrierClosure(intptr_t* sp) NOT_PRODUCT(: _sp(sp)) {} 376 377 virtual void do_oop(oop* p) override { compressedOopsWithBitmap ? do_oop_work((narrowOop*)p) : do_oop_work(p); } 378 virtual void do_oop(narrowOop* p) override { do_oop_work(p); } 379 380 template <class T> inline void do_oop_work(T* p) { 381 oop value = (oop)HeapAccess<>::oop_load(p); 382 if (barrier == stackChunkOopDesc::BarrierType::Store) { 383 HeapAccess<>::oop_store(p, value); 384 } 385 } 386 }; 387 388 template <stackChunkOopDesc::BarrierType barrier, ChunkFrames frame_kind, typename RegisterMapT> 389 void stackChunkOopDesc::do_barriers0(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { 390 // We need to invoke the write barriers so as not to miss oops in old chunks that haven't yet been concurrently scanned 391 assert (!f.is_done(), ""); 392 393 if (f.is_interpreted()) { 394 Method* m = f.to_frame().interpreter_frame_method(); 395 // Class redefinition support 396 m->record_gc_epoch(); 397 } else if (f.is_compiled()) { 398 nmethod* nm = f.cb()->as_nmethod(); 399 // The entry barrier takes care of having the right synchronization 400 // when keeping the nmethod alive during concurrent execution. 401 nm->run_nmethod_entry_barrier(); 402 // There is no need to mark the Method, as class redefinition will walk the 403 // CodeCache, noting their Methods 404 } 405 406 if (has_bitmap() && UseCompressedOops) { 407 BarrierClosure<barrier, true> oops_closure(f.sp()); 408 f.iterate_oops(&oops_closure, map); 409 } else { 410 BarrierClosure<barrier, false> oops_closure(f.sp()); 411 f.iterate_oops(&oops_closure, map); 412 } 413 } 414 415 template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Load> (const StackChunkFrameStream<ChunkFrames::Mixed>& f, const RegisterMap* map); 416 template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Store>(const StackChunkFrameStream<ChunkFrames::Mixed>& f, const RegisterMap* map); 417 template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Load> (const StackChunkFrameStream<ChunkFrames::CompiledOnly>& f, const RegisterMap* map); 418 template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Store>(const StackChunkFrameStream<ChunkFrames::CompiledOnly>& f, const RegisterMap* map); 419 template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Load> (const StackChunkFrameStream<ChunkFrames::Mixed>& f, const SmallRegisterMap* map); 420 template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Store>(const StackChunkFrameStream<ChunkFrames::Mixed>& f, const SmallRegisterMap* map); 421 template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Load> (const StackChunkFrameStream<ChunkFrames::CompiledOnly>& f, const SmallRegisterMap* map); 422 template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Store>(const StackChunkFrameStream<ChunkFrames::CompiledOnly>& f, const SmallRegisterMap* map); 423 424 template <typename RegisterMapT> 425 void stackChunkOopDesc::fix_thawed_frame(const frame& f, const RegisterMapT* map) { 426 if (!(is_gc_mode() || requires_barriers())) { 427 return; 428 } 429 430 BarrierSetStackChunk* bs_chunk = BarrierSet::barrier_set()->barrier_set_stack_chunk(); 431 FrameOopIterator<RegisterMapT> iterator(f, map); 432 bs_chunk->decode_gc_mode(this, &iterator); 433 434 if (f.is_compiled_frame() && f.oop_map()->has_derived_oops()) { 435 DerivedPointersSupport::DerelativizeClosure derived_closure; 436 OopMapDo<OopClosure, DerivedPointersSupport::DerelativizeClosure, SkipNullValue> visitor(nullptr, &derived_closure); 437 visitor.oops_do(&f, map, f.oop_map()); 438 } 439 } 440 441 template void stackChunkOopDesc::fix_thawed_frame(const frame& f, const RegisterMap* map); 442 template void stackChunkOopDesc::fix_thawed_frame(const frame& f, const SmallRegisterMap* map); 443 444 void stackChunkOopDesc::transfer_lockstack(oop* dst) { 445 const bool requires_gc_barriers = is_gc_mode() || requires_barriers(); 446 const bool requires_uncompress = has_bitmap() && UseCompressedOops; 447 const auto load_and_clear_obj = [&](intptr_t* at) -> oop { 448 if (requires_gc_barriers) { 449 if (requires_uncompress) { 450 oop value = HeapAccess<>::oop_load(reinterpret_cast<narrowOop*>(at)); 451 HeapAccess<>::oop_store(reinterpret_cast<narrowOop*>(at), nullptr); 452 return value; 453 } else { 454 oop value = HeapAccess<>::oop_load(reinterpret_cast<oop*>(at)); 455 HeapAccess<>::oop_store(reinterpret_cast<oop*>(at), nullptr); 456 return value; 457 } 458 } else { 459 oop value = *reinterpret_cast<oop*>(at); 460 HeapAccess<>::oop_store(reinterpret_cast<oop*>(at), nullptr); 461 return value; 462 } 463 }; 464 465 const int cnt = lockstack_size(); 466 intptr_t* lockstack_start = start_address(); 467 for (int i = 0; i < cnt; i++) { 468 oop mon_owner = load_and_clear_obj(&lockstack_start[i]); 469 assert(oopDesc::is_oop(mon_owner), "not an oop"); 470 dst[i] = mon_owner; 471 } 472 } 473 474 void stackChunkOopDesc::print_on(bool verbose, outputStream* st) const { 475 if (*((juint*)this) == badHeapWordVal) { 476 st->print_cr("BAD WORD"); 477 } else { 478 InstanceStackChunkKlass::print_chunk(const_cast<stackChunkOopDesc*>(this), verbose, st); 479 } 480 } 481 482 #ifdef ASSERT 483 484 class StackChunkVerifyOopsClosure : public OopClosure { 485 stackChunkOop _chunk; 486 int _count; 487 488 public: 489 StackChunkVerifyOopsClosure(stackChunkOop chunk) 490 : _chunk(chunk), _count(0) {} 491 492 void do_oop(oop* p) override { (_chunk->has_bitmap() && UseCompressedOops) ? do_oop_work((narrowOop*)p) : do_oop_work(p); } 493 void do_oop(narrowOop* p) override { do_oop_work(p); } 494 495 template <typename T> inline void do_oop_work(T* p) { 496 _count++; 497 oop obj = _chunk->load_oop(p); 498 assert(obj == nullptr || dbg_is_good_oop(obj), "p: " PTR_FORMAT " obj: " PTR_FORMAT, p2i(p), p2i(obj)); 499 if (_chunk->has_bitmap()) { 500 BitMap::idx_t index = _chunk->bit_index_for(p); 501 assert(_chunk->bitmap().at(index), "Bit not set at index " SIZE_FORMAT " corresponding to " PTR_FORMAT, index, p2i(p)); 502 } 503 } 504 505 int count() const { return _count; } 506 }; 507 508 class VerifyStackChunkFrameClosure { 509 stackChunkOop _chunk; 510 511 public: 512 intptr_t* _sp; 513 CodeBlob* _cb; 514 bool _callee_interpreted; 515 int _size; 516 int _argsize; 517 int _num_oops; 518 int _num_frames; 519 int _num_interpreted_frames; 520 int _num_i2c; 521 522 VerifyStackChunkFrameClosure(stackChunkOop chunk) 523 : _chunk(chunk), _sp(nullptr), _cb(nullptr), _callee_interpreted(false), 524 _size(0), _argsize(0), _num_oops(0), _num_frames(0), _num_interpreted_frames(0), _num_i2c(0) {} 525 526 template <ChunkFrames frame_kind, typename RegisterMapT> 527 bool do_frame(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { 528 _sp = f.sp(); 529 _cb = f.cb(); 530 531 int fsize = f.frame_size() - ((f.is_interpreted() == _callee_interpreted) ? _argsize : 0); 532 int num_oops = f.num_oops(); 533 assert(num_oops >= 0, ""); 534 535 _argsize = f.stack_argsize() + frame::metadata_words_at_top; 536 _size += fsize; 537 _num_oops += num_oops; 538 if (f.is_interpreted()) { 539 _num_interpreted_frames++; 540 } 541 542 log_develop_trace(continuations)("debug_verify_stack_chunk frame: %d sp: " INTPTR_FORMAT " pc: " PTR_FORMAT " interpreted: %d size: %d argsize: %d oops: %d", _num_frames, f.sp() - _chunk->start_address(), p2i(f.pc()), f.is_interpreted(), fsize, _argsize, num_oops); 543 LogTarget(Trace, continuations) lt; 544 if (lt.develop_is_enabled()) { 545 LogStream ls(lt); 546 f.print_on(&ls); 547 } 548 assert(f.pc() != nullptr, 549 "young: %d num_frames: %d sp: " PTR_FORMAT " start: " PTR_FORMAT " end: " PTR_FORMAT, 550 !_chunk->requires_barriers(), _num_frames, p2i(f.sp()), p2i(_chunk->start_address()), p2i(_chunk->bottom_address())); 551 552 if (_num_frames == 0) { 553 assert(f.pc() == _chunk->pc(), ""); 554 } 555 556 if (_num_frames > 0 && !_callee_interpreted && f.is_interpreted()) { 557 log_develop_trace(continuations)("debug_verify_stack_chunk i2c"); 558 _num_i2c++; 559 } 560 561 StackChunkVerifyOopsClosure oops_closure(_chunk); 562 f.iterate_oops(&oops_closure, map); 563 assert(oops_closure.count() == num_oops, "oops: %d oopmap->num_oops(): %d", oops_closure.count(), num_oops); 564 565 _callee_interpreted = f.is_interpreted(); 566 _num_frames++; 567 return true; 568 } 569 }; 570 571 template <typename T> 572 class StackChunkVerifyBitmapClosure : public BitMapClosure { 573 stackChunkOop _chunk; 574 575 public: 576 int _count; 577 578 StackChunkVerifyBitmapClosure(stackChunkOop chunk) : _chunk(chunk), _count(0) {} 579 580 bool do_bit(BitMap::idx_t index) override { 581 T* p = _chunk->address_for_bit<T>(index); 582 _count++; 583 584 oop obj = _chunk->load_oop(p); 585 assert(obj == nullptr || dbg_is_good_oop(obj), 586 "p: " PTR_FORMAT " obj: " PTR_FORMAT " index: " SIZE_FORMAT, 587 p2i(p), p2i((oopDesc*)obj), index); 588 589 return true; // continue processing 590 } 591 }; 592 593 bool stackChunkOopDesc::verify(size_t* out_size, int* out_oops, int* out_frames, int* out_interpreted_frames) { 594 DEBUG_ONLY(if (!VerifyContinuations) return true;) 595 596 assert(oopDesc::is_oop(this), ""); 597 598 assert(stack_size() >= 0, ""); 599 assert(!has_bitmap() || is_gc_mode(), ""); 600 601 if (is_empty()) { 602 assert(max_thawing_size() == 0, ""); 603 } else { 604 assert(argsize() >= 0, ""); 605 } 606 607 assert(oopDesc::is_oop_or_null(parent()), ""); 608 609 const bool concurrent = !Thread::current()->is_Java_thread(); 610 611 // If argsize == 0 and the chunk isn't mixed, the chunk contains the metadata (pc, fp -- frame::sender_sp_offset) 612 // for the top frame (below sp), and *not* for the bottom frame. 613 int size = bottom() - sp(); 614 assert(size >= 0, ""); 615 assert((size == 0) == is_empty(), ""); 616 617 const StackChunkFrameStream<ChunkFrames::Mixed> first(this); 618 619 VerifyStackChunkFrameClosure closure(this); 620 iterate_stack(&closure); 621 622 assert(!is_empty() || closure._cb == nullptr, ""); 623 if (closure._cb != nullptr && closure._cb->is_nmethod()) { 624 assert(argsize() == 625 (closure._cb->as_nmethod()->num_stack_arg_slots()*VMRegImpl::stack_slot_size) >>LogBytesPerWord, 626 "chunk argsize: %d bottom frame argsize: %d", argsize(), 627 (closure._cb->as_nmethod()->num_stack_arg_slots()*VMRegImpl::stack_slot_size) >>LogBytesPerWord); 628 } 629 630 assert(closure._num_interpreted_frames == 0 || has_mixed_frames(), ""); 631 632 if (!concurrent) { 633 assert(closure._size <= size + (stack_size() - bottom()), 634 "size: %d bottom: %d closure.size: %d end sp: " PTR_FORMAT " start sp: %d chunk size: %d", 635 size, bottom(), closure._size, closure._sp - start_address(), sp(), stack_size()); 636 if (closure._num_frames > 0) { 637 assert(closure._argsize >= frame::metadata_words_at_top, "should be set up"); 638 assert(argsize() == closure._argsize - frame::metadata_words_at_top, 639 "argsize(): %d closure.argsize: %d closure.callee_interpreted: %d", 640 argsize(), closure._argsize, closure._callee_interpreted); 641 } 642 643 int calculated_max_size = closure._size 644 + closure._num_i2c * frame::align_wiggle 645 + closure._num_interpreted_frames * frame::align_wiggle; 646 assert(max_thawing_size() == calculated_max_size, 647 "max_size(): %d calculated_max_size: %d argsize: %d num_i2c: %d", 648 max_thawing_size(), calculated_max_size, closure._argsize, closure._num_i2c); 649 650 if (out_size != nullptr) *out_size += size; 651 if (out_oops != nullptr) *out_oops += closure._num_oops; 652 if (out_frames != nullptr) *out_frames += closure._num_frames; 653 if (out_interpreted_frames != nullptr) *out_interpreted_frames += closure._num_interpreted_frames; 654 } else { 655 assert(out_size == nullptr, ""); 656 assert(out_oops == nullptr, ""); 657 assert(out_frames == nullptr, ""); 658 assert(out_interpreted_frames == nullptr, ""); 659 } 660 661 if (has_bitmap()) { 662 assert(bitmap().size() == InstanceStackChunkKlass::bitmap_size_in_bits(stack_size()), 663 "bitmap().size(): %zu stack_size: %d", 664 bitmap().size(), stack_size()); 665 666 int oop_count; 667 if (UseCompressedOops) { 668 StackChunkVerifyBitmapClosure<narrowOop> bitmap_closure(this); 669 bitmap().iterate(&bitmap_closure, 670 bit_index_for((narrowOop*)(sp_address() - frame::metadata_words_at_bottom)), 671 bit_index_for((narrowOop*)end_address())); 672 oop_count = bitmap_closure._count; 673 } else { 674 StackChunkVerifyBitmapClosure<oop> bitmap_closure(this); 675 bitmap().iterate(&bitmap_closure, 676 bit_index_for((oop*)(sp_address() - frame::metadata_words_at_bottom)), 677 bit_index_for((oop*)end_address())); 678 oop_count = bitmap_closure._count; 679 } 680 assert(oop_count == closure._num_oops, 681 "bitmap_closure._count: %d closure._num_oops: %d", oop_count, closure._num_oops); 682 } 683 684 return true; 685 } 686 #endif --- EOF ---