mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Move stack levels to pydevd (#1351)
This commit is contained in:
parent
69a76244bf
commit
46d0c7edf7
8 changed files with 80 additions and 37 deletions
|
|
@ -117,10 +117,10 @@ class PyDevdAPI(object):
|
|||
py_db.post_method_as_internal_command(
|
||||
thread_id, internal_get_completions, seq, thread_id, frame_id, act_tok, line=line, column=column)
|
||||
|
||||
def request_stack(self, py_db, seq, thread_id, fmt=None, timeout=.5):
|
||||
def request_stack(self, py_db, seq, thread_id, fmt=None, timeout=.5, start_frame=0, levels=0):
|
||||
# If it's already suspended, get it right away.
|
||||
internal_get_thread_stack = InternalGetThreadStack(
|
||||
seq, thread_id, py_db, set_additional_thread_info, fmt=fmt, timeout=timeout)
|
||||
seq, thread_id, py_db, set_additional_thread_info, fmt=fmt, timeout=timeout, start_frame=start_frame, levels=levels)
|
||||
if internal_get_thread_stack.can_be_executed_by(get_current_thread_id(threading.current_thread())):
|
||||
internal_get_thread_stack.do_it(py_db)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -550,13 +550,15 @@ class InternalGetThreadStack(InternalThreadCommand):
|
|||
stopped in a breakpoint).
|
||||
'''
|
||||
|
||||
def __init__(self, seq, thread_id, py_db, set_additional_thread_info, fmt, timeout=.5):
|
||||
def __init__(self, seq, thread_id, py_db, set_additional_thread_info, fmt, timeout=.5, start_frame=0, levels=0):
|
||||
InternalThreadCommand.__init__(self, thread_id)
|
||||
self._py_db = weakref.ref(py_db)
|
||||
self._timeout = time.time() + timeout
|
||||
self.seq = seq
|
||||
self._cmd = None
|
||||
self._fmt = fmt
|
||||
self._start_frame = start_frame
|
||||
self._levels = levels
|
||||
|
||||
# Note: receives set_additional_thread_info to avoid a circular import
|
||||
# in this module.
|
||||
|
|
@ -574,7 +576,7 @@ class InternalGetThreadStack(InternalThreadCommand):
|
|||
frame = additional_info.get_topmost_frame(t)
|
||||
try:
|
||||
self._cmd = py_db.cmd_factory.make_get_thread_stack_message(
|
||||
py_db, self.seq, self.thread_id, frame, self._fmt, must_be_suspended=not timed_out)
|
||||
py_db, self.seq, self.thread_id, frame, self._fmt, must_be_suspended=not timed_out, start_frame=self._start_frame, levels=self._levels)
|
||||
finally:
|
||||
frame = None
|
||||
t = None
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ class NetCommandFactoryJson(NetCommandFactory):
|
|||
return frame_name
|
||||
|
||||
@overrides(NetCommandFactory.make_get_thread_stack_message)
|
||||
def make_get_thread_stack_message(self, py_db, seq, thread_id, topmost_frame, fmt, must_be_suspended=False):
|
||||
def make_get_thread_stack_message(self, py_db, seq, thread_id, topmost_frame, fmt, must_be_suspended=False, start_frame=0, levels=0):
|
||||
frames = []
|
||||
module_events = []
|
||||
if topmost_frame is not None:
|
||||
|
|
@ -204,11 +204,18 @@ class NetCommandFactoryJson(NetCommandFactory):
|
|||
for module_event in module_events:
|
||||
py_db.writer.add_command(module_event)
|
||||
|
||||
total_frames = len(frames)
|
||||
stack_frames = frames
|
||||
if bool(levels):
|
||||
start = start_frame
|
||||
end = min(start + levels, total_frames)
|
||||
stack_frames = frames[start:end]
|
||||
|
||||
response = pydevd_schema.StackTraceResponse(
|
||||
request_seq=seq,
|
||||
success=True,
|
||||
command='stackTrace',
|
||||
body=pydevd_schema.StackTraceResponseBody(stackFrames=frames, totalFrames=len(frames)))
|
||||
body=pydevd_schema.StackTraceResponseBody(stackFrames=stack_frames, totalFrames=total_frames))
|
||||
return NetCommand(CMD_RETURN, 0, response, is_json=True)
|
||||
|
||||
@overrides(NetCommandFactory.make_io_message)
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ class NetCommandFactory(object):
|
|||
except:
|
||||
return self.make_error_message(seq, get_exception_traceback_str())
|
||||
|
||||
def make_get_thread_stack_message(self, py_db, seq, thread_id, topmost_frame, fmt, must_be_suspended=False):
|
||||
def make_get_thread_stack_message(self, py_db, seq, thread_id, topmost_frame, fmt, must_be_suspended=False, start_frame=0, levels=0):
|
||||
"""
|
||||
Returns thread stack as XML.
|
||||
|
||||
|
|
|
|||
|
|
@ -481,11 +481,13 @@ class _PyDevJsonCommandProcessor(object):
|
|||
# : :type stack_trace_arguments: StackTraceArguments
|
||||
stack_trace_arguments = request.arguments
|
||||
thread_id = stack_trace_arguments.threadId
|
||||
start_frame = stack_trace_arguments.startFrame
|
||||
levels = stack_trace_arguments.levels
|
||||
|
||||
fmt = stack_trace_arguments.format
|
||||
if hasattr(fmt, 'to_dict'):
|
||||
fmt = fmt.to_dict()
|
||||
self.api.request_stack(py_db, request.seq, thread_id, fmt)
|
||||
self.api.request_stack(py_db, request.seq, thread_id, fmt=fmt, start_frame=start_frame, levels=levels)
|
||||
|
||||
def on_exceptioninfo_request(self, py_db, request):
|
||||
'''
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
def method1(n):
|
||||
if n <= 0:
|
||||
return 0 # Break here
|
||||
method2(n - 1)
|
||||
|
||||
|
||||
def method2(n):
|
||||
method1(n - 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
method1(100)
|
||||
except:
|
||||
pass # Don't let it print the exception (just deal with caught exceptions).
|
||||
print('TEST SUCEEDED!')
|
||||
|
|
@ -1182,6 +1182,41 @@ def test_exception_details(case_setup, max_frames):
|
|||
|
||||
writer.finished_ok = True
|
||||
|
||||
def test_stack_levels(case_setup):
|
||||
with case_setup.test_file('_debugger_case_deep_stacks.py') as writer:
|
||||
json_facade = JsonFacade(writer)
|
||||
|
||||
writer.write_set_protocol('http_json')
|
||||
writer.write_add_breakpoint(writer.get_line_index_with_content('Break here'))
|
||||
|
||||
json_facade.write_make_initial_run()
|
||||
hit = writer.wait_for_breakpoint_hit()
|
||||
|
||||
# get full stack
|
||||
stack_trace_request = json_facade.write_request(
|
||||
pydevd_schema.StackTraceRequest(pydevd_schema.StackTraceArguments(threadId=hit.thread_id)))
|
||||
stack_trace_response = json_facade.wait_for_response(stack_trace_request)
|
||||
full_stack_frames = stack_trace_response.body.stackFrames
|
||||
total_frames = stack_trace_response.body.totalFrames
|
||||
|
||||
startFrame = 0
|
||||
levels = 20
|
||||
received_frames = []
|
||||
while startFrame < total_frames:
|
||||
stack_trace_request = json_facade.write_request(
|
||||
pydevd_schema.StackTraceRequest(pydevd_schema.StackTraceArguments(
|
||||
threadId=hit.thread_id,
|
||||
startFrame=startFrame,
|
||||
levels=20)))
|
||||
stack_trace_response = json_facade.wait_for_response(stack_trace_request)
|
||||
received_frames += stack_trace_response.body.stackFrames
|
||||
startFrame += levels
|
||||
|
||||
assert full_stack_frames == received_frames
|
||||
|
||||
writer.write_run_thread(hit.thread_id)
|
||||
|
||||
writer.finished_ok = True
|
||||
|
||||
@pytest.mark.skipif(IS_JYTHON, reason='No goto on Jython.')
|
||||
def test_goto(case_setup):
|
||||
|
|
|
|||
|
|
@ -1412,7 +1412,15 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
|
|||
def _forward_request_to_pydevd(self, request, args):
|
||||
translate_thread_id = args.get('threadId') is not None
|
||||
if translate_thread_id:
|
||||
pyd_tid = self.thread_map.to_pydevd(int(args['threadId']))
|
||||
try:
|
||||
vsc_tid = int(args['threadId'])
|
||||
pyd_tid = self.thread_map.to_pydevd(vsc_tid)
|
||||
except KeyError:
|
||||
# Unknown thread, nothing much we can do about it here
|
||||
self.send_error_response(
|
||||
request,
|
||||
'Thread {} not found'.format(vsc_tid))
|
||||
return
|
||||
|
||||
pydevd_request = copy.deepcopy(request)
|
||||
del pydevd_request['seq'] # A new seq should be created for pydevd.
|
||||
|
|
@ -1480,35 +1488,8 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
|
|||
else:
|
||||
self.send_error_response(request, resp_args.get('message', 'Error retrieving source'))
|
||||
|
||||
@async_handler
|
||||
def on_stackTrace(self, request, args):
|
||||
vsc_tid = int(args['threadId'])
|
||||
|
||||
try:
|
||||
pyd_tid = self.thread_map.to_pydevd(vsc_tid)
|
||||
except KeyError:
|
||||
# Unknown thread, nothing much we can do about it here
|
||||
self.send_error_response(
|
||||
request,
|
||||
'Thread {} not found'.format(vsc_tid))
|
||||
return
|
||||
pydevd_request = copy.deepcopy(request)
|
||||
del pydevd_request['seq'] # A new seq should be created for pydevd.
|
||||
# Translate threadId for pydevd.
|
||||
pydevd_request['arguments']['threadId'] = pyd_tid
|
||||
_, _, resp_args = yield self.pydevd_request(
|
||||
pydevd_comm.CMD_GET_THREAD_STACK,
|
||||
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)
|
||||
self._forward_request_to_pydevd(request, args)
|
||||
|
||||
def on_scopes(self, request, args):
|
||||
self._forward_request_to_pydevd(request, args)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue