changeset 53257:a8fab0f44f6c cont

Walk stacks of unmounted nested continuations
author rpressler
date Wed, 02 Jan 2019 17:05:38 +0000
parents 0c0cafdaad34
children c0fa6641f5e6 10e98b552e0e
files make/hotspot/symbols/symbols-unix src/hotspot/share/include/jvm.h src/hotspot/share/prims/jvm.cpp src/hotspot/share/prims/stackwalk.cpp src/hotspot/share/prims/stackwalk.hpp src/hotspot/share/runtime/continuation.cpp src/hotspot/share/runtime/continuation.hpp src/hotspot/share/runtime/frame.cpp src/hotspot/share/runtime/registerMap.hpp src/hotspot/share/runtime/vframe.cpp src/hotspot/share/runtime/vframe.hpp src/java.base/share/classes/java/lang/Continuation.java src/java.base/share/classes/java/lang/LiveStackFrameInfo.java src/java.base/share/classes/java/lang/StackFrameInfo.java src/java.base/share/classes/java/lang/StackStreamFactory.java src/java.base/share/classes/java/lang/StackWalker.java src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java src/java.base/share/native/libjava/StackStreamFactory.c test/jdk/java/lang/Continuation/Basic.java test/jdk/java/lang/Continuation/Scoped.java
diffstat 21 files changed, 298 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/make/hotspot/symbols/symbols-unix	Sun Dec 30 08:13:42 2018 +0000
+++ b/make/hotspot/symbols/symbols-unix	Wed Jan 02 17:05:38 2019 +0000
@@ -174,6 +174,7 @@
 JVM_SetClassSigners
 JVM_SetNativeThreadName
 JVM_SetPrimitiveArrayElement
+JVM_SetStackWalkContinuation
 JVM_SetThreadPriority
 JVM_Sleep
 JVM_StartThread
--- a/src/hotspot/share/include/jvm.h	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/hotspot/share/include/jvm.h	Wed Jan 02 17:05:38 2019 +0000
@@ -211,6 +211,9 @@
                   jint frame_count, jint start_index, 
                   jobjectArray frames);
 
+JNIEXPORT void JNICALL
+JVM_SetStackWalkContinuation(JNIEnv *env, jobject stackStream, jlong anchor, jobjectArray frames, jobject cont);
+
 /*
  * java.lang.Thread
  */
--- a/src/hotspot/share/prims/jvm.cpp	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/hotspot/share/prims/jvm.cpp	Wed Jan 02 17:05:38 2019 +0000
@@ -601,7 +601,6 @@
                                   jint frame_count, jint start_index, 
                                   jobjectArray frames))
   JVMWrapper("JVM_MoreStackWalk");
-  JavaThread* jt = (JavaThread*) THREAD;
 
   // frames array is a Class<?>[] array when only getting caller reference,
   // and a StackFrameInfo[] array (or derivative) otherwise. It should never
@@ -619,6 +618,17 @@
                                   start_index, frames_array_h, THREAD);
 JVM_END
 
+JVM_ENTRY(void, JVM_SetStackWalkContinuation(JNIEnv *env, jobject stackStream, jlong anchor, jobjectArray frames, jobject cont))
+    JVMWrapper("JVM_SetStackWalkContinuation");
+
+    objArrayOop fa = objArrayOop(JNIHandles::resolve_non_null(frames));
+    objArrayHandle frames_array_h(THREAD, fa);
+    Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream));
+    Handle cont_h(THREAD, JNIHandles::resolve_non_null(cont));
+
+    StackWalk::setContinuation(stackStream_h, anchor, frames_array_h, cont_h, THREAD);
+JVM_END
+
 // java.lang.Object ///////////////////////////////////////////////
 
 
--- a/src/hotspot/share/prims/stackwalk.cpp	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/hotspot/share/prims/stackwalk.cpp	Wed Jan 02 17:05:38 2019 +0000
@@ -59,6 +59,14 @@
   return ok;
 }
 
+void BaseFrameStream::set_continuation(Handle cont) {
+  // ensure that the lifetime of the handle is that of the entire walk
+  // This actually also sets a copy of the handle in the RegisterMap,
+  // but that's OK, because we want them to be the same, anyway.
+  // (although we don't rely on this sharing, and set the other copy again)
+  *(_continuation.raw_value()) = cont(); 
+}
+
 // static inline Handle continuation_of(Handle cont_or_scope) {
 //   return (cont_or_scope.not_null() && cont_or_scope()->is_a(SystemDictionary::Continuation_klass()))
 //             ? cont_or_scope
@@ -76,15 +84,28 @@
   : BaseFrameStream(thread, cont), 
    _vfst(cont.is_null()
       ? vframeStream(thread, cont_scope)
-      : vframeStream(cont, cont_scope)) {
+      : vframeStream(cont)) {
   _need_method_info = StackWalk::need_method_info(mode);
 }
 
 LiveFrameStream::LiveFrameStream(JavaThread* thread, RegisterMap* rm, Handle cont_scope, Handle cont)
    : BaseFrameStream(thread, cont), _cont_scope(cont_scope) {
      
+    _map = rm;
     _jvf = cont.is_null() ? thread->last_java_vframe(rm)
-                          : Continuation::last_java_vframe(cont(), rm);
+                          : Continuation::last_java_vframe(cont, rm);
+}
+
+void JavaFrameStream::set_continuation(Handle cont) {
+  BaseFrameStream::set_continuation(cont);
+
+  _vfst = vframeStream(continuation()); // we must not use the handle argument (lifetime; see BaseFrameStream::set_continuation)
+}
+
+void LiveFrameStream::set_continuation(Handle cont) {
+  BaseFrameStream::set_continuation(cont);
+
+  _jvf = Continuation::last_java_vframe(continuation(), _map); // we must not use the handle argument (lifetime; see BaseFrameStream::set_continuation)
 }
 
 void JavaFrameStream::next() { _vfst.next();}
@@ -396,7 +417,6 @@
     Klass* abstractStackWalker_klass = SystemDictionary::AbstractStackWalker_klass();
     while (!stream.at_end()) {
       InstanceKlass* ik = stream.method()->method_holder();
-      // XXXXXX skip stackWalker frames XXXXXXX
       if (ik != stackWalker_klass &&
             ik != abstractStackWalker_klass && ik->super() != abstractStackWalker_klass)  {
         break;
@@ -520,3 +540,18 @@
   }
   return end_index;
 }
+
+void StackWalk::setContinuation(Handle stackStream, jlong magic, objArrayHandle frames_array, Handle cont, TRAPS) {
+  JavaThread* jt = (JavaThread*)THREAD;
+
+  if (frames_array.is_null()) {
+    THROW_MSG(vmSymbols::java_lang_NullPointerException(), "frames_array is NULL");
+  }
+
+  BaseFrameStream* existing_stream = BaseFrameStream::from_current(jt, magic, frames_array);
+  if (existing_stream == NULL) {
+    THROW_MSG(vmSymbols::java_lang_InternalError(), "doStackWalk: corrupted buffers");
+  }
+
+  existing_stream->set_continuation(cont);
+}
--- a/src/hotspot/share/prims/stackwalk.hpp	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/hotspot/share/prims/stackwalk.hpp	Wed Jan 02 17:05:38 2019 +0000
@@ -60,6 +60,9 @@
 
   virtual void    fill_frame(int index, objArrayHandle  frames_array,
                              const methodHandle& method, TRAPS)=0;
+  
+  Handle continuation() { return _continuation; }
+  virtual void set_continuation(Handle cont);
 
   void setup_magic_on_entry(objArrayHandle frames_array);
   bool check_magic(objArrayHandle frames_array);
@@ -92,6 +95,8 @@
 
   void fill_frame(int index, objArrayHandle  frames_array,
                   const methodHandle& method, TRAPS);
+
+  void set_continuation(Handle cont);
 };
 
 class LiveFrameStream : public BaseFrameStream {
@@ -101,6 +106,7 @@
     MODE_COMPILED    = 0x02
   };
 
+  RegisterMap*          _map;
   javaVFrame*           _jvf;
   Handle                _cont_scope;
 
@@ -121,6 +127,8 @@
 
   void fill_frame(int index, objArrayHandle  frames_array,
                   const methodHandle& method, TRAPS);
+
+  void set_continuation(Handle cont);
 };
 
 class StackWalk : public AllStatic {
@@ -158,5 +166,8 @@
   static jint fetchNextBatch(Handle stackStream, jlong mode, jlong magic,
                              int frame_count, int start_index,
                              objArrayHandle frames_array, TRAPS);
+
+  static void setContinuation(Handle stackStream, jlong magic, objArrayHandle frames_array, 
+                              Handle cont, TRAPS);
 };
 #endif // SHARE_VM_PRIMS_STACKWALK_HPP
--- a/src/hotspot/share/runtime/continuation.cpp	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/hotspot/share/runtime/continuation.cpp	Wed Jan 02 17:05:38 2019 +0000
@@ -2340,10 +2340,11 @@
 
 typedef int (*DoYieldStub)(int scopes);
 
+// called in a safepoint
 int Continuation::try_force_yield(JavaThread* thread, oop cont) {
-
+  // this is the only place where we traverse the continuatuion hierarchy in native code, as it needs to be done in a safepoint
+  oop scope = NULL;
   oop innermost = get_continuation(thread);
-  oop scope = NULL;
   for (oop c = innermost; c != NULL; c = java_lang_Continuation::parent(c)) {
     if (c == cont) {
       scope = java_lang_Continuation::scope(c);
@@ -2356,7 +2357,7 @@
   if (thread->_cont_yield) {
     return -2; // during yield
   }
-  if (innermost != cont) {
+  if (!oopDesc::equals(innermost, cont)) {
     java_lang_Continuation::set_yieldInfo(cont, scope);
   }
   
@@ -3090,19 +3091,22 @@
 
   // tty->print_cr("continuation_top_frame");
   
-  map->set_cont(map->thread(), contOop);
+  if (!oopDesc::equals(map->cont(), contOop))
+    map->set_cont(map->thread(), contOop);
+
   return hf.to_frame(cont);
 }
 
 static frame continuation_parent_frame(ContMirror& cont, RegisterMap* map) {
   assert (map->thread() != NULL || cont.entryPC() == NULL, "");
-  if (map->thread() == NULL) { // When a continuation is mounted, its entry frame is always on the v-stack
-    oop parentOop = java_lang_Continuation::parent(cont.mirror());
-    if (parentOop != NULL) {
-      // tty->print_cr("continuation_parent_frame: parent");
-      return continuation_top_frame(parentOop, map);
-    }
-  }
+
+  // if (map->thread() == NULL) { // When a continuation is mounted, its entry frame is always on the v-stack
+  //   oop parentOop = java_lang_Continuation::parent(cont.mirror());
+  //   if (parentOop != NULL) {
+  //     // tty->print_cr("continuation_parent_frame: parent");
+  //     return continuation_top_frame(parentOop, map);
+  //   }
+  // }
   
   map->set_cont(map->thread(), NULL);
   if (cont.entryPC() == NULL) { // When we're walking an unmounted continuation and reached the end
@@ -3118,18 +3122,19 @@
   return sender;
 }
 
-frame Continuation::last_frame(oop continutation, RegisterMap *map) {
+frame Continuation::last_frame(Handle continuation, RegisterMap *map) {
   assert(map != NULL, "a map must be given");
-  return continuation_top_frame(continutation, map);
+  map->set_cont(continuation);
+  return continuation_top_frame(continuation(), map);
 }
 
-bool Continuation::has_last_Java_frame(oop continutation) {
-  return (java_lang_Continuation::sp(continutation) >= 0);
+bool Continuation::has_last_Java_frame(Handle continuation) {
+  return java_lang_Continuation::pc(continuation()) != NULL;
 }
 
-javaVFrame* Continuation::last_java_vframe(oop continutation, RegisterMap *map) {
+javaVFrame* Continuation::last_java_vframe(Handle continuation, RegisterMap *map) {
   assert(map != NULL, "a map must be given");
-  frame f = last_frame(continutation, map);
+  frame f = last_frame(continuation, map);
   for (vframe* vf = vframe::new_vframe(&f, map, NULL); vf; vf = vf->sender()) {
     if (vf->is_java_frame()) return javaVFrame::cast(vf);
   }
--- a/src/hotspot/share/runtime/continuation.hpp	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/hotspot/share/runtime/continuation.hpp	Wed Jan 02 17:05:38 2019 +0000
@@ -69,9 +69,9 @@
   static frame sender_for_interpreter_frame(const frame& callee, RegisterMap* map);
   static frame sender_for_compiled_frame(const frame& callee, RegisterMap* map);
 
-  static bool has_last_Java_frame(oop continutation);
-  static frame last_frame(oop continutation, RegisterMap *map);
-  static javaVFrame* last_java_vframe(oop continutation, RegisterMap *map);
+  static bool has_last_Java_frame(Handle continuation);
+  static frame last_frame(Handle continuation, RegisterMap *map);
+  static javaVFrame* last_java_vframe(Handle continuation, RegisterMap *map);
 
   // access frame data
   static address usp_offset_to_location(const frame& fr, const RegisterMap* map, const int usp_offset_in_bytes);
--- a/src/hotspot/share/runtime/frame.cpp	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/hotspot/share/runtime/frame.cpp	Wed Jan 02 17:05:38 2019 +0000
@@ -77,6 +77,7 @@
   _walk_cont     = map->_walk_cont;
 
   _cont = map->_cont;
+
   pd_initialize_from(map);
   if (update_map()) {
     for(int i = 0; i < location_valid_size; i++) {
@@ -98,7 +99,7 @@
 
 void RegisterMap::set_cont(Thread* thread, oop cont) {
   // tty->print_cr("set_cont: %d", cont != NULL);
-  _cont = cont != NULL ? Handle(thread != NULL ? thread : Thread::current(), cont) : Handle();
+  set_cont(cont != NULL ? Handle(thread != NULL ? thread : Thread::current(), cont) : Handle());
 }
 
 void RegisterMap::clear() {
@@ -111,6 +112,7 @@
   } else {
     pd_initialize();
   }
+  _cont = Handle();
 }
 
 #ifndef PRODUCT
--- a/src/hotspot/share/runtime/registerMap.hpp	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/hotspot/share/runtime/registerMap.hpp	Wed Jan 02 17:05:38 2019 +0000
@@ -134,6 +134,7 @@
   JavaThread *thread() const { return _thread; }
   oop  cont()          const { return _cont(); }
   void set_cont(Thread* thread, oop cont);
+  void set_cont(Handle cont) {  _cont = cont; }
   bool update_map()    const { return _update_map; }
   bool walk_cont()     const { return _walk_cont; }
   bool validate_oops() const { return _validate_oops; }
--- a/src/hotspot/share/runtime/vframe.cpp	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/hotspot/share/runtime/vframe.cpp	Wed Jan 02 17:05:38 2019 +0000
@@ -504,18 +504,18 @@
   }
 }
 
-vframeStream::vframeStream(Handle continuation, Handle continuation_scope) 
+vframeStream::vframeStream(Handle continuation) 
  : vframeStreamCommon(RegisterMap(NULL, false, true)) {
 
   _stop_at_java_call_stub = false;
-  _continuation_scope = continuation_scope;
+  _continuation_scope = Handle();
   
-  if (!Continuation::has_last_Java_frame(continuation())) {
+  if (!Continuation::has_last_Java_frame(continuation)) {
     _mode = at_end_mode;
     return;
   }
 
-  _frame = Continuation::last_frame(continuation(), &_reg_map);
+  _frame = Continuation::last_frame(continuation, &_reg_map);
   while (!fill_from_frame()) {
     _frame = _frame.sender(&_reg_map);
   }
--- a/src/hotspot/share/runtime/vframe.hpp	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/hotspot/share/runtime/vframe.hpp	Wed Jan 02 17:05:38 2019 +0000
@@ -357,7 +357,7 @@
   // top_frame may not be at safepoint, start with sender
   vframeStream(JavaThread* thread, frame top_frame, bool stop_at_java_call_stub = false);
 
-  vframeStream(Handle continuation, Handle continuation_scope);
+  vframeStream(Handle continuation);
 };
 
 #endif // SHARE_VM_RUNTIME_VFRAME_HPP
--- a/src/java.base/share/classes/java/lang/Continuation.java	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/java.base/share/classes/java/lang/Continuation.java	Wed Jan 02 17:05:38 2019 +0000
@@ -38,6 +38,7 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
 
 /**
  * TBD
@@ -69,7 +70,11 @@
 
         final Pinned pinned;
         private PreemptStatus(Pinned reason) { this.pinned = reason; }
-        Pinned pinned() { return pinned; }
+        /** 
+         * TBD
+         * @return TBD
+         **/
+        public Pinned pinned() { return pinned; }
     }
 
     private static Thread currentCarrierThread() {
@@ -87,8 +92,13 @@
         }
     }
 
+    private Runnable target;
+
+    /* While the native JVM code is aware that every continuation has a scope, it is, for the most part,
+     * oblivious to the continuation hierarchy. The only time this hierarchy is traversed in native code
+     * is when a hierarchy of continuations is mounted on the native stack.
+     */
     private final ContinuationScope scope;
-    private Runnable target;
     private Continuation parent; // null for native stack
     private Continuation child; // non-null when we're yielded in a child continuation
 
@@ -151,6 +161,14 @@
         return super.toString() + " scope: " + scope;
     }
 
+    ContinuationScope getScope() {
+        return scope;
+    }
+
+    Continuation getParent() {
+        return parent;
+    }
+
     /**
      * TBD
      * @param scope TBD
@@ -220,6 +238,22 @@
         return stackWalker().walk(s -> s.map(StackWalker.StackFrame::toStackTraceElement).toArray(StackTraceElement[]::new));
     }
 
+    /// Support for StackWalker
+    static <R> R wrapWalk(Continuation inner, ContinuationScope scope, Supplier<R> walk) {
+        try {
+            for (Continuation c = inner; c != null && c.scope != scope; c = c.parent)
+                c.mount();
+
+            // if (!inner.isStarted())
+            //     throw new IllegalStateException("Continuation not started");
+                
+            return walk.get();
+        } finally {
+            for (Continuation c = inner; c != null && c.scope != scope; c = c.parent)
+                c.unmount();
+        }
+    }
+
     private Continuation innermost() {
         Continuation c = this;
         while (c.child != null)
@@ -227,12 +261,12 @@
         return c;
     }
 
-    void mount() {
+    private void mount() {
         if (!compareAndSetMounted(false, true))
             throw new IllegalStateException("Mounted!!!!");
     }
 
-    void unmount() {
+    private void unmount() {
         setMounted(false);
     }
     
@@ -307,7 +341,7 @@
         // This method runs in the "entry frame".
         // A yield jumps to this method's caller as if returning from this method.
         try {
-            if (stack == null || sp >= stack.length) { // is this the first run? (at this point we know !done)
+            if (!isStarted()) { // is this the first run? (at this point we know !done)
                 if (TRACE)
                     System.out.println("ENTERING " + id());
                 this.entrySP = getSP();
@@ -329,6 +363,10 @@
         target.run();
     }
 
+    private boolean isStarted() {
+        return stack != null && sp < stack.length;
+    }
+
     /**
      * TBD
      * 
--- a/src/java.base/share/classes/java/lang/LiveStackFrameInfo.java	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/java.base/share/classes/java/lang/LiveStackFrameInfo.java	Wed Jan 02 17:05:38 2019 +0000
@@ -42,6 +42,15 @@
     private int mode = 0;
 
     @Override
+    protected void clear() {
+        super.clear();
+        monitors = EMPTY_ARRAY;
+        locals = EMPTY_ARRAY;
+        operands = EMPTY_ARRAY;
+        mode = 0;
+    }
+
+    @Override
     public Object[] getMonitors() {
         return monitors;
     }
--- a/src/java.base/share/classes/java/lang/StackFrameInfo.java	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/java.base/share/classes/java/lang/StackFrameInfo.java	Wed Jan 02 17:05:38 2019 +0000
@@ -29,6 +29,7 @@
 
 import java.lang.StackWalker.StackFrame;
 import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
 
 class StackFrameInfo implements StackFrame {
     private final byte RETAIN_CLASS_REF = 0x01;
@@ -37,8 +38,8 @@
         SharedSecrets.getJavaLangInvokeAccess();
 
     private final byte flags;
-    private final Object memberName;
-    private final short bci;
+    private Object memberName;
+    private short bci;
     private volatile StackTraceElement ste;
 
     /*
@@ -57,6 +58,17 @@
         return JLIA.getDeclaringClass(memberName);
     }
 
+    void setMemberName(Method method) {
+        this.memberName = JLIA.newMemberName(method);
+    }
+
+    void setBCI(short bci) {
+        this.bci = bci;
+    }
+
+    protected void clear() {
+    }
+
     // ----- implementation of StackFrame methods
 
     @Override
--- a/src/java.base/share/classes/java/lang/StackStreamFactory.java	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/java.base/share/classes/java/lang/StackStreamFactory.java	Wed Jan 02 17:05:38 2019 +0000
@@ -32,6 +32,8 @@
 import java.lang.annotation.Native;
 import java.lang.reflect.Method;
 import java.lang.reflect.Constructor;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.HashSet;
 import java.util.NoSuchElementException;
 import java.util.Objects;
@@ -127,7 +129,7 @@
         protected FrameBuffer<? extends T> frameBuffer;
         protected long anchor;
         protected final ContinuationScope contScope;
-        protected final Continuation continuation;
+        protected Continuation continuation;
 
         // buffers to fill in stack frame information
         protected AbstractStackWalker(StackWalker walker, int mode) {
@@ -240,8 +242,12 @@
          */
         final R walk() {
             checkState(NEW);
-            Continuation cont = walker.getContinuation();
-            if (cont != null) cont.mount();
+            return continuation != null
+                ? Continuation.wrapWalk(continuation, contScope, this::walkHelper)
+                : walkHelper();
+        }
+
+        private final R walkHelper() {
             try {
                 // VM will need to stablize the stack before walking.  It will invoke
                 // the AbstractStackWalker::doStackWalk method once it fetches the first batch.
@@ -249,7 +255,6 @@
                 return beginStackWalk();
             } finally {
                 close();  // done traversal; close the stream
-                if (cont != null) cont.unmount();
             }
         }
 
@@ -307,6 +312,12 @@
             }
 
             this.anchor = anchor;  // set anchor for this bulk stack frame traversal
+
+            int numFrames = bufEndIndex - bufStartIndex;        
+            if (numFrames > 0 && numFrames < batchSize && continuation != null) {
+                makeContinuationEnterFrame(bufEndIndex);
+                bufEndIndex++;
+            }
             frameBuffer.setBatch(depth, bufStartIndex, bufEndIndex);
 
             // traverse all frames and perform the action on the stack frames, if specified
@@ -318,7 +329,8 @@
          */
         private int getNextBatch() {
             int nextBatchSize = Math.min(maxDepth - depth, getNextBatchSize());
-            if (!frameBuffer.isActive() || nextBatchSize <= 0) {
+
+            if (!frameBuffer.isActive() || nextBatchSize <= 0 || (frameBuffer.isAtBottom() && !hasMoreContinuations())) {
                 if (isDebug) {
                     System.out.format("  more stack walk done%n");
                 }
@@ -326,7 +338,24 @@
                 return 0;
             }
 
-            return fetchStackFrames(nextBatchSize);
+            if (frameBuffer.isAtBottom() && hasMoreContinuations()) {
+                setContinuation(continuation.getParent());
+            }
+
+            int numFrames = fetchStackFrames(nextBatchSize);
+            if (numFrames == 0 && !hasMoreContinuations()) {
+                frameBuffer.freeze(); // done stack walking
+            }
+            return numFrames;
+        }
+
+        private boolean hasMoreContinuations() {
+            return continuation != null && continuation.getScope() != contScope && continuation.getParent() != null;
+        }
+
+        private void setContinuation(Continuation cont) {
+            this.continuation = cont;
+            setContinuation(anchor, frameBuffer.frames(), cont);
         }
 
         /*
@@ -398,16 +427,27 @@
                 System.out.format("  more stack walk requesting %d got %d to %d frames%n",
                                   batchSize, frameBuffer.startIndex(), endIndex);
             }
+
             int numFrames = endIndex - startIndex;
-            if (numFrames == 0) {
-                frameBuffer.freeze(); // done stack walking
-            } else {
+
+            if (numFrames < batchSize && continuation != null) {
+                makeContinuationEnterFrame(endIndex);
+                endIndex++;
+                numFrames++;
+            }
+
+            if (numFrames > 0) {
                 frameBuffer.setBatch(depth, startIndex, endIndex);
             }
             return numFrames;
         }
 
         /**
+         * Create a synthetic frame for {@code Continuation.enter}.
+         */
+        protected abstract void makeContinuationEnterFrame(int index);
+
+        /**
          * Begins stack walking.  This method anchors this frame and invokes
          * AbstractStackWalker::doStackWalk after fetching the first batch of stack frames.
          *
@@ -441,6 +481,8 @@
         private native int fetchStackFrames(long mode, long anchor,
                                             int batchSize, int startIndex,
                                             T[] frames);
+
+        private native void setContinuation(long anchor, T[] frames, Continuation cont);
     }
 
     /*
@@ -510,6 +552,22 @@
             }
         }
 
+        @Override
+        protected final void makeContinuationEnterFrame(int index) {
+            PrivilegedAction<Method> pa = () -> {
+                try {
+                    return Continuation.class.getDeclaredMethod("enter");
+                } catch(NoSuchMethodException | SecurityException e) {
+                    throw new AssertionError(e);
+                }
+            };
+            Method enter = AccessController.doPrivileged(pa);
+            StackFrameInfo sfi = frameBuffer.frames()[index];
+            sfi.clear();
+            sfi.setMemberName(enter);
+            sfi.setBCI((short)-1);
+        }
+
         final Function<? super Stream<StackFrame>, ? extends T> function;  // callback
 
         StackFrameTraverser(StackWalker walker,
@@ -716,6 +774,11 @@
         protected int getNextBatchSize() {
             return MIN_BATCH_SIZE;
         }
+
+        @Override
+        protected final void makeContinuationEnterFrame(int index) {
+            throw new InternalError("should not reach here");
+        }
     }
 
     static final class LiveStackInfoTraverser<T> extends StackFrameTraverser<T> {
@@ -892,7 +955,15 @@
          * it is done for traversal.  All stack frames have been traversed.
          */
         final boolean isActive() {
-            return origin > 0 && (fence == 0 || origin < fence || fence == currentBatchSize);
+            return origin > 0; //  && (fence == 0 || origin < fence || fence == currentBatchSize);
+        }
+
+        /*
+         * Tests if this frame buffer is at the end of the stack
+         * and all frames have been traversed.
+         */
+        final boolean isAtBottom() {
+            return origin > 0 && origin >= fence && fence < currentBatchSize;
         }
 
         /**
--- a/src/java.base/share/classes/java/lang/StackWalker.java	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/java.base/share/classes/java/lang/StackWalker.java	Wed Jan 02 17:05:38 2019 +0000
@@ -677,6 +677,9 @@
             throw new UnsupportedOperationException("This stack walker " +
                     "does not have RETAIN_CLASS_REFERENCE access");
         }
+        if (continuation != null) {
+            throw new UnsupportedOperationException("This stack walker walks a continuation");
+        }
 
         return StackStreamFactory.makeCallerFinder(this).findCaller();
     }
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Jan 02 17:05:38 2019 +0000
@@ -40,6 +40,7 @@
 import sun.invoke.util.Wrapper;
 
 import java.lang.reflect.Array;
+import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -1732,6 +1733,11 @@
             }
 
             @Override
+            public Object newMemberName(Method method) {
+                return new MemberName(method);
+            }
+
+            @Override
             public String getName(Object mname) {
                 MemberName memberName = (MemberName)mname;
                 return memberName.getName();
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java	Wed Jan 02 17:05:38 2019 +0000
@@ -26,6 +26,7 @@
 package jdk.internal.access;
 
 import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
 import java.util.Map;
 
 public interface JavaLangInvokeAccess {
@@ -35,6 +36,11 @@
     Object newMemberName();
 
     /**
+     * Create a new MemberName instance. Used by {@code StackFrameInfo}.
+     */
+    Object newMemberName(Method method);
+
+    /**
      * Returns the name for the given MemberName. Used by {@code StackFrameInfo}.
      */
     String getName(Object mname);
--- a/src/java.base/share/native/libjava/StackStreamFactory.c	Sun Dec 30 08:13:42 2018 +0000
+++ b/src/java.base/share/native/libjava/StackStreamFactory.c	Wed Jan 02 17:05:38 2019 +0000
@@ -75,3 +75,14 @@
     return JVM_MoreStackWalk(env, stackstream, mode, anchor, batchSize, 
                              startIndex, frames);
 }
+
+/*
+ * Class:     java_lang_StackStreamFactory_AbstractStackWalker
+ * Method:    setContinuation
+ * Signature: (J[Ljava/lang/Object;Ljava/lang/Continuation;)V
+ */
+JNIEXPORT void JNICALL Java_java_lang_StackStreamFactory_00024AbstractStackWalker_setContinuation
+  (JNIEnv *env, jobject stackstream, jlong anchor, jobjectArray frames, jobject cont)
+{
+  JVM_SetStackWalkContinuation(env, stackstream, anchor, frames, cont);
+}
--- a/test/jdk/java/lang/Continuation/Basic.java	Sun Dec 30 08:13:42 2018 +0000
+++ b/test/jdk/java/lang/Continuation/Basic.java	Wed Jan 02 17:05:38 2019 +0000
@@ -98,7 +98,7 @@
         walker = StackWalker.getInstance(FOO);
         frames = walker.walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
 
-        assertEquals(frames, Arrays.asList("bar", "foo", "lambda$test1$0", "enter0"));
+        assertEquals(frames, Arrays.asList("bar", "foo", "lambda$test1$0", "enter0", "enter"));
 
         long r = b+1;
         return "" + r;
--- a/test/jdk/java/lang/Continuation/Scoped.java	Sun Dec 30 08:13:42 2018 +0000
+++ b/test/jdk/java/lang/Continuation/Scoped.java	Wed Jan 02 17:05:38 2019 +0000
@@ -45,10 +45,10 @@
 
 @Test
 public class Scoped {
-        static final ContinuationScope A = new ContinuationScope() {};
-        static final ContinuationScope B = new ContinuationScope() {};
-        static final ContinuationScope C = new ContinuationScope() {};
-        static final ContinuationScope K = new ContinuationScope() {};
+    static final ContinuationScope A = new ContinuationScope("A") {};
+    static final ContinuationScope B = new ContinuationScope("B") {};
+    static final ContinuationScope C = new ContinuationScope("C") {};
+    static final ContinuationScope K = new ContinuationScope("K") {};
 
     public void test1() {
                 final AtomicInteger res = new AtomicInteger(0);
@@ -62,35 +62,38 @@
                         res.set((int)r);        
                 });
 
+                List<String> frames;
+                frames = cont.stackWalker().walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
+                // System.out.println("No scope (before start): " + frames);
+                assertEquals(frames, List.of());
+
                 while (!cont.isDone()) {
                         cont.run();
                         System.gc();
 
-                        List<String> frames;
-
                         frames = cont.stackWalker().walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
-                        System.out.println("No scope: " + frames);
-                        // assertEquals(frames, Arrays.asList("yield0", "yield", "bar", "foo", "lambda$test1$0", "enter0"));
+                        // System.out.println("No scope: " + frames);
+                        assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "enter0", "enter", "run", "bar", "lambda$foo$8", "enter0", "enter", "run", "foo", "lambda$test1$0", "enter0", "enter"));
 
                         frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), A).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
-                        System.out.println("A: " + frames);
-                        // assertEquals(frames, Arrays.asList("yield0", "yield", "bar", "foo", "lambda$test1$0", "enter0"));
+                        // System.out.println("A: " + frames);
+                        assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "enter0", "enter", "run", "bar", "lambda$foo$8", "enter0", "enter", "run", "foo", "lambda$test1$0", "enter0", "enter"));
 
                         frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), B).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
-                        System.out.println("B: " + frames);
-                        // assertEquals(frames, Arrays.asList("yield0", "yield", "bar", "foo", "lambda$test1$0", "enter0"));
+                        // System.out.println("B: " + frames);
+                        assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "enter0", "enter", "run", "bar", "lambda$foo$8", "enter0", "enter"));
 
                         frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), C).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
-                        System.out.println("C: " + frames);
-                        // assertEquals(frames, Arrays.asList("yield0", "yield"));
+                        // System.out.println("C: " + frames);
+                        assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "enter0", "enter"));
 
                         frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), K).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
-                        System.out.println("K: " + frames);
-                        // assertEquals(frames, Arrays.asList("yield0", "yield"));
+                        // System.out.println("K: " + frames);
+                        assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "enter0", "enter", "run", "bar", "lambda$foo$8", "enter0", "enter", "run", "foo", "lambda$test1$0", "enter0", "enter"));
 
                         frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), null).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
-                        System.out.println("null: " + frames);
-                        // assertEquals(frames, Arrays.asList("yield0", "yield"));
+                        // System.out.println("null: " + frames);
+                        assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "enter0", "enter", "run", "bar", "lambda$foo$8", "enter0", "enter", "run", "foo", "lambda$test1$0", "enter0", "enter"));
                 }
                 assertEquals(res.get(), 2);
         }
@@ -116,27 +119,27 @@
                         StackWalker walker = StackWalker.getInstance();
                         List<String> frames = walker.walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
                 
-                        assertEquals(frames.subList(0, 15), Arrays.asList("lambda$bar$6", "enter0", "enter", "run", "bar", "lambda$foo$1", "enter0", "enter", "run", "foo", "lambda$test1$0", "enter0", "enter", "run", "test1"));
+                        assertEquals(frames.subList(0, 15), Arrays.asList("lambda$bar$14", "enter0", "enter", "run", "bar", "lambda$foo$8", "enter0", "enter", "run", "foo", "lambda$test1$0", "enter0", "enter", "run", "test1"));
                 
                         walker = StackWalker.getInstance(C);
                         frames = walker.walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
                 
-                        assertEquals(frames, Arrays.asList("lambda$bar$6", "enter0"));
+                        assertEquals(frames, Arrays.asList("lambda$bar$14", "enter0"));
 
                         walker = StackWalker.getInstance(B);
                         frames = walker.walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
                 
-                        assertEquals(frames, Arrays.asList("lambda$bar$6", "enter0", "enter", "run", "bar", "lambda$foo$1", "enter0"));
+                        assertEquals(frames, Arrays.asList("lambda$bar$14", "enter0", "enter", "run", "bar", "lambda$foo$8", "enter0"));
 
                         walker = StackWalker.getInstance(A);
                         frames = walker.walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
                 
-                        assertEquals(frames, Arrays.asList("lambda$bar$6", "enter0", "enter", "run", "bar", "lambda$foo$1", "enter0", "enter", "run", "foo", "lambda$test1$0", "enter0"));
+                        assertEquals(frames, Arrays.asList("lambda$bar$14", "enter0", "enter", "run", "bar", "lambda$foo$8", "enter0", "enter", "run", "foo", "lambda$test1$0", "enter0"));
 
                         walker = StackWalker.getInstance(K);
                         frames = walker.walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
                 
-                        assertEquals(frames, Arrays.asList("lambda$bar$6", "enter0", "enter", "run", "bar", "lambda$foo$1", "enter0", "enter", "run", "foo", "lambda$test1$0", "enter0"));
+                        assertEquals(frames.subList(0, 15), Arrays.asList("lambda$bar$14", "enter0", "enter", "run", "bar", "lambda$foo$8", "enter0", "enter", "run", "foo", "lambda$test1$0", "enter0", "enter", "run", "test1"));
 
                         long r = b+1;
                 });