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