Repository: microsoft/ptvsd

Issue:
Add support for Delayed Stack Trace Loading
Enable `supportsDelayedStackTraceLoading` and ensure we support `startFrame` and `levels` arguments of the `StackTraceRequest`.


Discussion:
This should be done after merging #1092 

Resolving patch:
diff --git a/src/ptvsd/wrapper.py b/src/ptvsd/wrapper.py
--- a/src/ptvsd/wrapper.py
+++ b/src/ptvsd/wrapper.py
@@ -958,6 +958,7 @@ def _stop_event_loop(self):
     supportsConditionalBreakpoints=True,
     supportsConfigurationDoneRequest=True,
     supportsDebuggerProperties=True,
+    supportsDelayedStackTraceLoading=True,
     supportsEvaluateForHovers=True,
     supportsExceptionInfoRequest=True,
     supportsExceptionOptions=True,
@@ -1562,7 +1563,6 @@ def on_source(self, request, args):
 
     @async_handler
     def on_stackTrace(self, request, args):
-        # TODO: docstring
         vsc_tid = int(args['threadId'])
 
         try:
@@ -1582,7 +1582,12 @@ def on_stackTrace(self, request, args):
             pydevd_request,
             is_json=True)
 
+        levels = int(args.get('levels', 0))
         stackFrames = resp_args['body']['stackFrames']
+        if levels > 0:
+            start = int(args.get('startFrame', 0))
+            end = min(start + levels, len(stackFrames))
+            stackFrames = resp_args['body']['stackFrames'][start:end]
         totalFrames = resp_args['body']['totalFrames']
         self.send_response(request, stackFrames=stackFrames, totalFrames=totalFrames)
 


Test patch:
diff --git a/tests/func/test_breakpoints.py b/tests/func/test_breakpoints.py
--- a/tests/func/test_breakpoints.py
+++ b/tests/func/test_breakpoints.py
@@ -480,3 +480,53 @@ def code_to_debug():
         assert not expected_bps
 
         session.wait_for_exit()
+
+
+def test_deep_stacks(pyfile, run_as, start_method):
+    @pyfile
+    def code_to_debug():
+        from dbgimporter import import_and_enable_debugger
+        import_and_enable_debugger()
+
+        def deep_stack(level):
+            if level <= 0:
+                print('done')  #@bp
+                return level
+            deep_stack(level - 1)
+        deep_stack(100)
+
+    line_numbers = get_marked_line_numbers(code_to_debug)
+    with DebugSession() as session:
+        session.initialize(
+            target=(run_as, code_to_debug),
+            start_method=start_method,
+            ignore_unobserved=[Event('continued')],
+        )
+
+        bp_line = line_numbers['bp']
+
+        actual_bps = session.set_breakpoints(code_to_debug, [bp_line])
+        actual_bps = [bp['line'] for bp in actual_bps]
+        session.start_debugging()
+
+        hit = session.wait_for_thread_stopped()
+        full_frames = hit.stacktrace.body['stackFrames']
+        assert len(full_frames) > 100
+
+        # Construct stack from parts
+        frames = []
+        start = 0
+        for _ in range(5):
+            resp_stacktrace = session.send_request('stackTrace', arguments={
+                'threadId': hit.thread_id,
+                'startFrame': start,
+                'levels': 25
+            }).wait_for_response()
+            assert resp_stacktrace.body['totalFrames'] > 0
+            frames += resp_stacktrace.body['stackFrames']
+            start = len(frames)
+
+        assert full_frames == frames
+
+        session.send_request('continue').wait_for_response()
+        session.wait_for_exit()