1 /* 2 * Copyright (c) 2018, 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 "classfile/vmSymbols.hpp" 27 #include "gc/shared/barrierSetNMethod.hpp" 28 #include "oops/method.inline.hpp" 29 #include "runtime/continuation.hpp" 30 #include "runtime/continuationEntry.inline.hpp" 31 #include "runtime/continuationHelper.inline.hpp" 32 #include "runtime/continuationJavaClasses.inline.hpp" 33 #include "runtime/continuationWrapper.inline.hpp" 34 #include "runtime/interfaceSupport.inline.hpp" 35 #include "runtime/javaThread.inline.hpp" 36 #include "runtime/osThread.hpp" 37 #include "runtime/vframe.inline.hpp" 38 #include "runtime/vframe_hp.hpp" 39 40 // defined in continuationFreezeThaw.cpp 41 extern "C" jint JNICALL CONT_isPinned0(JNIEnv* env, jobject cont_scope); 42 43 JVM_ENTRY(void, CONT_pin(JNIEnv* env, jclass cls)) { 44 if (!Continuation::pin(JavaThread::thread_from_jni_environment(env))) { 45 THROW_MSG(vmSymbols::java_lang_IllegalStateException(), "pin overflow"); 46 } 47 } 48 JVM_END 49 50 JVM_ENTRY(void, CONT_unpin(JNIEnv* env, jclass cls)) { 51 if (!Continuation::unpin(JavaThread::thread_from_jni_environment(env))) { 52 THROW_MSG(vmSymbols::java_lang_IllegalStateException(), "pin underflow"); 53 } 54 } 55 JVM_END 56 57 #ifndef PRODUCT 58 static jlong java_tid(JavaThread* thread) { 59 return java_lang_Thread::thread_id(thread->threadObj()); 60 } 61 #endif 62 63 ContinuationEntry* Continuation::get_continuation_entry_for_continuation(JavaThread* thread, oop continuation) { 64 if (thread == nullptr || continuation == nullptr) { 65 return nullptr; 66 } 67 68 for (ContinuationEntry* entry = thread->last_continuation(); entry != nullptr; entry = entry->parent()) { 69 if (continuation == entry->cont_oop(thread)) { 70 return entry; 71 } 72 } 73 return nullptr; 74 } 75 76 static bool is_on_stack(JavaThread* thread, const ContinuationEntry* entry) { 77 if (entry == nullptr) { 78 return false; 79 } 80 81 assert(thread->is_in_full_stack((address)entry), ""); 82 return true; 83 // return false if called when transitioning to Java on return from freeze 84 // return !thread->has_last_Java_frame() || thread->last_Java_sp() < cont->entry_sp(); 85 } 86 87 bool Continuation::is_continuation_mounted(JavaThread* thread, oop continuation) { 88 return is_on_stack(thread, get_continuation_entry_for_continuation(thread, continuation)); 89 } 90 91 // When walking the virtual stack, this method returns true 92 // iff the frame is a thawed continuation frame whose 93 // caller is still frozen on the h-stack. 94 // The continuation object can be extracted from the thread. 95 bool Continuation::is_cont_barrier_frame(const frame& f) { 96 assert(f.is_interpreted_frame() || f.cb() != nullptr, ""); 97 if (!Continuations::enabled()) return false; 98 return is_return_barrier_entry(f.is_interpreted_frame() ? ContinuationHelper::InterpretedFrame::return_pc(f) 99 : ContinuationHelper::CompiledFrame::return_pc(f)); 100 } 101 102 bool Continuation::is_return_barrier_entry(const address pc) { 103 if (!Continuations::enabled()) return false; 104 return pc == StubRoutines::cont_returnBarrier(); 105 } 106 107 bool Continuation::is_continuation_enterSpecial(const frame& f) { 108 if (f.cb() == nullptr || !f.cb()->is_nmethod()) { 109 return false; 110 } 111 Method* m = f.cb()->as_nmethod()->method(); 112 return (m != nullptr && m->is_continuation_enter_intrinsic()); 113 } 114 115 bool Continuation::is_continuation_entry_frame(const frame& f, const RegisterMap *map) { 116 // we can do this because the entry frame is never inlined 117 Method* m = (map != nullptr && map->in_cont() && f.is_interpreted_frame()) 118 ? map->stack_chunk()->interpreter_frame_method(f) 119 : ContinuationHelper::Frame::frame_method(f); 120 return m != nullptr && m->intrinsic_id() == vmIntrinsics::_Continuation_enter; 121 } 122 123 // The parameter `sp` should be the actual sp and not the unextended sp because at 124 // least on PPC64 unextended_sp < sp is possible as interpreted frames are trimmed 125 // to the actual size of the expression stack before calls. The problem there is 126 // that even unextended_sp < entry_sp < sp is possible for an interpreted frame. 127 static inline bool is_sp_in_continuation(const ContinuationEntry* entry, intptr_t* const sp) { 128 // entry_sp() returns the unextended_sp which is always greater or equal to the actual sp 129 return entry->entry_sp() > sp; 130 } 131 132 bool Continuation::is_frame_in_continuation(const ContinuationEntry* entry, const frame& f) { 133 return is_sp_in_continuation(entry, f.sp()); 134 } 135 136 ContinuationEntry* Continuation::get_continuation_entry_for_sp(JavaThread* thread, intptr_t* const sp) { 137 assert(thread != nullptr, ""); 138 ContinuationEntry* entry = thread->last_continuation(); 139 while (entry != nullptr && !is_sp_in_continuation(entry, sp)) { 140 entry = entry->parent(); 141 } 142 return entry; 143 } 144 145 ContinuationEntry* Continuation::get_continuation_entry_for_entry_frame(JavaThread* thread, const frame& f) { 146 assert(is_continuation_enterSpecial(f), ""); 147 ContinuationEntry* entry = (ContinuationEntry*)f.unextended_sp(); 148 assert(entry == get_continuation_entry_for_sp(thread, f.sp()-2), "mismatched entry"); 149 return entry; 150 } 151 152 bool Continuation::is_frame_in_continuation(JavaThread* thread, const frame& f) { 153 return f.is_heap_frame() || (get_continuation_entry_for_sp(thread, f.sp()) != nullptr); 154 } 155 156 static frame continuation_top_frame(const ContinuationWrapper& cont, RegisterMap* map) { 157 stackChunkOop chunk = cont.last_nonempty_chunk(); 158 map->set_stack_chunk(chunk); 159 return chunk != nullptr ? chunk->top_frame(map) : frame(); 160 } 161 162 bool Continuation::has_last_Java_frame(oop continuation, frame* frame, RegisterMap* map) { 163 ContinuationWrapper cont(continuation); 164 if (!cont.is_empty()) { 165 *frame = continuation_top_frame(cont, map); 166 return true; 167 } else { 168 return false; 169 } 170 } 171 172 frame Continuation::last_frame(oop continuation, RegisterMap *map) { 173 assert(map != nullptr, "a map must be given"); 174 return continuation_top_frame(ContinuationWrapper(continuation), map); 175 } 176 177 frame Continuation::top_frame(const frame& callee, RegisterMap* map) { 178 assert(map != nullptr, ""); 179 ContinuationEntry* ce = get_continuation_entry_for_sp(map->thread(), callee.sp()); 180 assert(ce != nullptr, ""); 181 oop continuation = ce->cont_oop(map->thread()); 182 ContinuationWrapper cont(continuation); 183 return continuation_top_frame(cont, map); 184 } 185 186 javaVFrame* Continuation::last_java_vframe(Handle continuation, RegisterMap *map) { 187 assert(map != nullptr, "a map must be given"); 188 if (!ContinuationWrapper(continuation()).is_empty()) { 189 frame f = last_frame(continuation(), map); 190 for (vframe* vf = vframe::new_vframe(&f, map, nullptr); vf; vf = vf->sender()) { 191 if (vf->is_java_frame()) { 192 return javaVFrame::cast(vf); 193 } 194 } 195 } 196 return nullptr; 197 } 198 199 frame Continuation::continuation_parent_frame(RegisterMap* map) { 200 assert(map->in_cont(), ""); 201 ContinuationWrapper cont(map); 202 assert(map->thread() != nullptr || !cont.is_mounted(), ""); 203 204 log_develop_trace(continuations)("continuation_parent_frame"); 205 if (map->update_map()) { 206 // we need to register the link address for the entry frame 207 if (cont.entry() != nullptr) { 208 cont.entry()->update_register_map(map); 209 } else { 210 map->clear(); 211 } 212 } 213 214 if (!cont.is_mounted()) { // When we're walking an unmounted continuation and reached the end 215 oop parent = jdk_internal_vm_Continuation::parent(cont.continuation()); 216 stackChunkOop chunk = parent != nullptr ? ContinuationWrapper(parent).last_nonempty_chunk() : nullptr; 217 if (chunk != nullptr) { 218 return chunk->top_frame(map); 219 } 220 221 map->set_stack_chunk(nullptr); 222 return frame(); 223 } 224 225 map->set_stack_chunk(nullptr); 226 227 #if (defined(X86) || defined(AARCH64) || defined(RISCV64) || defined(PPC64)) && !defined(ZERO) 228 frame sender(cont.entrySP(), cont.entryFP(), cont.entryPC()); 229 #else 230 frame sender = frame(); 231 Unimplemented(); 232 #endif 233 234 return sender; 235 } 236 237 oop Continuation::continuation_scope(oop continuation) { 238 return continuation != nullptr ? jdk_internal_vm_Continuation::scope(continuation) : nullptr; 239 } 240 241 bool Continuation::is_scope_bottom(oop cont_scope, const frame& f, const RegisterMap* map) { 242 if (cont_scope == nullptr || !is_continuation_entry_frame(f, map)) { 243 return false; 244 } 245 246 oop continuation; 247 if (map->in_cont()) { 248 continuation = map->cont(); 249 } else { 250 ContinuationEntry* ce = get_continuation_entry_for_sp(map->thread(), f.sp()); 251 if (ce == nullptr) { 252 return false; 253 } 254 continuation = ce->cont_oop(map->thread()); 255 } 256 if (continuation == nullptr) { 257 return false; 258 } 259 260 oop sc = continuation_scope(continuation); 261 assert(sc != nullptr, ""); 262 return sc == cont_scope; 263 } 264 265 bool Continuation::is_in_usable_stack(address addr, const RegisterMap* map) { 266 ContinuationWrapper cont(map); 267 stackChunkOop chunk = cont.find_chunk_by_address(addr); 268 return chunk != nullptr ? chunk->is_usable_in_chunk(addr) : false; 269 } 270 271 bool Continuation::pin(JavaThread* current) { 272 ContinuationEntry* ce = current->last_continuation(); 273 if (ce == nullptr) { 274 return true; // no continuation mounted 275 } 276 return ce->pin(); 277 } 278 279 bool Continuation::unpin(JavaThread* current) { 280 ContinuationEntry* ce = current->last_continuation(); 281 if (ce == nullptr) { 282 return true; // no continuation mounted 283 } 284 return ce->unpin(); 285 } 286 287 frame Continuation::continuation_bottom_sender(JavaThread* thread, const frame& callee, intptr_t* sender_sp) { 288 assert (thread != nullptr, ""); 289 ContinuationEntry* ce = get_continuation_entry_for_sp(thread, callee.sp()); 290 assert(ce != nullptr, "callee.sp(): " INTPTR_FORMAT, p2i(callee.sp())); 291 292 log_develop_debug(continuations)("continuation_bottom_sender: [" JLONG_FORMAT "] [%d] callee: " INTPTR_FORMAT 293 " sender_sp: " INTPTR_FORMAT, 294 java_tid(thread), thread->osthread()->thread_id(), p2i(callee.sp()), p2i(sender_sp)); 295 296 frame entry = ce->to_frame(); 297 if (callee.is_interpreted_frame()) { 298 entry.set_sp(sender_sp); // sp != unextended_sp 299 } 300 return entry; 301 } 302 303 address Continuation::get_top_return_pc_post_barrier(JavaThread* thread, address pc) { 304 ContinuationEntry* ce; 305 if (thread != nullptr && is_return_barrier_entry(pc) && (ce = thread->last_continuation()) != nullptr) { 306 return ce->entry_pc(); 307 } 308 return pc; 309 } 310 311 void Continuation::set_cont_fastpath_thread_state(JavaThread* thread) { 312 assert(thread != nullptr, ""); 313 bool fast = !thread->is_interp_only_mode(); 314 thread->set_cont_fastpath_thread_state(fast); 315 } 316 317 void Continuation::notify_deopt(JavaThread* thread, intptr_t* sp) { 318 ContinuationEntry* entry = thread->last_continuation(); 319 320 if (entry == nullptr) { 321 return; 322 } 323 324 if (is_sp_in_continuation(entry, sp)) { 325 thread->push_cont_fastpath(sp); 326 return; 327 } 328 329 ContinuationEntry* prev; 330 do { 331 prev = entry; 332 entry = entry->parent(); 333 } while (entry != nullptr && !is_sp_in_continuation(entry, sp)); 334 335 if (entry == nullptr) { 336 return; 337 } 338 assert(is_sp_in_continuation(entry, sp), ""); 339 if (sp > prev->parent_cont_fastpath()) { 340 prev->set_parent_cont_fastpath(sp); 341 } 342 } 343 344 #ifndef PRODUCT 345 void Continuation::describe(FrameValues &values) { 346 JavaThread* thread = JavaThread::active(); 347 if (thread != nullptr) { 348 for (ContinuationEntry* ce = thread->last_continuation(); ce != nullptr; ce = ce->parent()) { 349 intptr_t* bottom = ce->entry_sp(); 350 if (bottom != nullptr) { 351 values.describe(-1, bottom, "continuation entry"); 352 } 353 } 354 } 355 } 356 #endif 357 358 #ifdef ASSERT 359 void Continuation::debug_verify_continuation(oop contOop) { 360 if (!VerifyContinuations) { 361 return; 362 } 363 assert(contOop != nullptr, ""); 364 assert(oopDesc::is_oop(contOop), ""); 365 ContinuationWrapper cont(contOop); 366 367 assert(oopDesc::is_oop_or_null(cont.tail()), ""); 368 assert(cont.chunk_invariant(), ""); 369 370 bool nonempty_chunk = false; 371 size_t max_size = 0; 372 int num_chunks = 0; 373 int num_frames = 0; 374 int num_interpreted_frames = 0; 375 int num_oops = 0; 376 377 for (stackChunkOop chunk = cont.tail(); chunk != nullptr; chunk = chunk->parent()) { 378 log_develop_trace(continuations)("debug_verify_continuation chunk %d", num_chunks); 379 chunk->verify(&max_size, &num_oops, &num_frames, &num_interpreted_frames); 380 if (!chunk->is_empty()) { 381 nonempty_chunk = true; 382 } 383 num_chunks++; 384 } 385 386 const bool is_empty = cont.is_empty(); 387 assert(!nonempty_chunk || !is_empty, ""); 388 assert(is_empty == (!nonempty_chunk && cont.last_frame().is_empty()), ""); 389 } 390 391 void Continuation::print(oop continuation) { print_on(tty, continuation); } 392 393 void Continuation::print_on(outputStream* st, oop continuation) { 394 ContinuationWrapper cont(continuation); 395 396 st->print_cr("CONTINUATION: " PTR_FORMAT " done: %d", 397 continuation->identity_hash(), jdk_internal_vm_Continuation::done(continuation)); 398 st->print_cr("CHUNKS:"); 399 for (stackChunkOop chunk = cont.tail(); chunk != nullptr; chunk = chunk->parent()) { 400 st->print("* "); 401 chunk->print_on(true, st); 402 } 403 } 404 #endif // ASSERT 405 406 407 void continuations_init() { Continuations::init(); } 408 409 void Continuations::init() { 410 Continuation::init(); 411 } 412 413 bool Continuations::enabled() { 414 return VMContinuations; 415 } 416 417 #define CC (char*) /*cast a literal from (const char*)*/ 418 #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) 419 420 static JNINativeMethod CONT_methods[] = { 421 {CC"pin", CC"()V", FN_PTR(CONT_pin)}, 422 {CC"unpin", CC"()V", FN_PTR(CONT_unpin)}, 423 {CC"isPinned0", CC"(Ljdk/internal/vm/ContinuationScope;)I", FN_PTR(CONT_isPinned0)}, 424 }; 425 426 void CONT_RegisterNativeMethods(JNIEnv *env, jclass cls) { 427 JavaThread* thread = JavaThread::current(); 428 ThreadToNativeFromVM trans(thread); 429 int status = env->RegisterNatives(cls, CONT_methods, sizeof(CONT_methods)/sizeof(JNINativeMethod)); 430 guarantee(status == JNI_OK, "register jdk.internal.vm.Continuation natives"); 431 guarantee(!env->ExceptionOccurred(), "register jdk.internal.vm.Continuation natives"); 432 }