mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Added multiThread option to enhance multithreaded debugging
This commit is contained in:
parent
ef9a67fe15
commit
ef4f122724
7 changed files with 36 additions and 9 deletions
|
|
@ -129,6 +129,9 @@ class PyDevdAPI(object):
|
|||
def set_gui_event_loop(self, py_db, gui_event_loop):
|
||||
py_db._gui_event_loop = gui_event_loop
|
||||
|
||||
def set_multi_thread(self, py_db, multi_thread):
|
||||
py_db.set_multi_thread(multi_thread)
|
||||
|
||||
def send_error_message(self, py_db, msg):
|
||||
cmd = py_db.cmd_factory.make_warning_message('pydevd: %s\n' % (msg,))
|
||||
py_db.writer.add_command(cmd)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class DebugOptions(object):
|
|||
'max_exception_stack_frames',
|
||||
'gui_event_loop',
|
||||
'client_os',
|
||||
'multi_thread'
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
|
|
@ -28,6 +29,7 @@ class DebugOptions(object):
|
|||
self.max_exception_stack_frames = 0
|
||||
self.gui_event_loop = 'matplotlib'
|
||||
self.client_os = None
|
||||
self.multi_thread = False
|
||||
|
||||
def to_json(self):
|
||||
dct = {}
|
||||
|
|
@ -60,6 +62,9 @@ class DebugOptions(object):
|
|||
if 'CLIENT_OS_TYPE' in debug_options:
|
||||
self.client_os = debug_options.get('CLIENT_OS_TYPE')
|
||||
|
||||
if 'MULTI_THREAD' in debug_options:
|
||||
self.multi_thread = debug_options.get('MULTI_THREAD')
|
||||
|
||||
# Note: _max_exception_stack_frames cannot be set by debug options.
|
||||
|
||||
def update_from_args(self, args):
|
||||
|
|
@ -99,6 +104,8 @@ class DebugOptions(object):
|
|||
if 'clientOS' in args:
|
||||
self.client_os = str(args['clientOS']).upper()
|
||||
|
||||
if 'multiThread' in args:
|
||||
self.multi_thread = bool_parser(args['multiThread'])
|
||||
|
||||
def int_parser(s, default_value=0):
|
||||
try:
|
||||
|
|
@ -128,6 +135,7 @@ DEBUG_OPTIONS_PARSER = {
|
|||
'STOP_ON_ENTRY': bool_parser,
|
||||
'SHOW_RETURN_VALUE': bool_parser,
|
||||
'MULTIPROCESS': bool_parser,
|
||||
'MULTI_THREAD': bool_parser,
|
||||
}
|
||||
|
||||
DEBUG_OPTIONS_BY_FLAG = {
|
||||
|
|
@ -145,6 +153,7 @@ DEBUG_OPTIONS_BY_FLAG = {
|
|||
'StopOnEntry': 'STOP_ON_ENTRY=True',
|
||||
'ShowReturnValue': 'SHOW_RETURN_VALUE=True',
|
||||
'Multiprocess': 'MULTIPROCESS=True',
|
||||
'MultiThread': 'MULTI_THREAD=False',
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -384,7 +384,7 @@ class NetCommandFactoryJson(NetCommandFactory):
|
|||
description=exc_desc,
|
||||
threadId=thread_id,
|
||||
text=exc_name,
|
||||
allThreadsStopped=True,
|
||||
allThreadsStopped= thread_id == '*' if self.multi_thread else True,
|
||||
preserveFocusHint=preserve_focus_hint,
|
||||
)
|
||||
event = pydevd_schema.StoppedEvent(body)
|
||||
|
|
@ -392,7 +392,7 @@ class NetCommandFactoryJson(NetCommandFactory):
|
|||
|
||||
@overrides(NetCommandFactory.make_thread_resume_single_notification)
|
||||
def make_thread_resume_single_notification(self, thread_id):
|
||||
body = ContinuedEventBody(threadId=thread_id, allThreadsContinued=True)
|
||||
body = ContinuedEventBody(threadId=thread_id, allThreadsContinued=thread_id == '*' if self.multi_thread else True)
|
||||
event = pydevd_schema.ContinuedEvent(body)
|
||||
return NetCommand(CMD_THREAD_RESUME_SINGLE_NOTIFICATION, 0, event, is_json=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class NetCommandFactory(object):
|
|||
|
||||
def __init__(self):
|
||||
self._additional_thread_id_to_thread_name = {}
|
||||
self.multi_thread = False
|
||||
|
||||
def _thread_to_xml(self, thread):
|
||||
""" thread information as XML """
|
||||
|
|
@ -508,3 +509,6 @@ This may mean a number of things:
|
|||
|
||||
def make_exit_command(self, py_db):
|
||||
return NULL_EXIT_COMMAND
|
||||
|
||||
def set_multi_thread(self, multi_thread):
|
||||
self.multi_thread = multi_thread
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ class PyDevJsonCommandProcessor(object):
|
|||
|
||||
def _set_debug_options(self, py_db, args, start_reason):
|
||||
rules = args.get('rules')
|
||||
stepping_resumes_all_threads = args.get('steppingResumesAllThreads', True)
|
||||
stepping_resumes_all_threads = not args.get('multiThread', False) and args.get('steppingResumesAllThreads', True)
|
||||
self.api.set_stepping_resumes_all_threads(py_db, stepping_resumes_all_threads)
|
||||
|
||||
terminate_child_processes = args.get('terminateChildProcesses', True)
|
||||
|
|
@ -472,6 +472,7 @@ class PyDevJsonCommandProcessor(object):
|
|||
self.api.stop_on_entry()
|
||||
|
||||
self.api.set_gui_event_loop(py_db, self._options.gui_event_loop)
|
||||
self.api.set_multi_thread(py_db, self._options.multi_thread)
|
||||
|
||||
def _send_process_event(self, py_db, start_method):
|
||||
argv = getattr(sys, 'argv', [])
|
||||
|
|
@ -735,7 +736,7 @@ class PyDevJsonCommandProcessor(object):
|
|||
|
||||
arguments = request.arguments # : :type arguments: SetFunctionBreakpointsArguments
|
||||
function_breakpoints = []
|
||||
suspend_policy = 'ALL'
|
||||
suspend_policy = 'NONE' if self._options.multi_thread else 'ALL'
|
||||
|
||||
# Not currently covered by the DAP.
|
||||
is_logpoint = False
|
||||
|
|
@ -775,7 +776,7 @@ class PyDevJsonCommandProcessor(object):
|
|||
self.api.remove_all_breakpoints(py_db, filename)
|
||||
|
||||
btype = 'python-line'
|
||||
suspend_policy = 'ALL'
|
||||
suspend_policy = 'NONE' if self._options.multi_thread else 'ALL'
|
||||
|
||||
if not filename.lower().endswith('.py'): # Note: check based on original file, not mapping.
|
||||
if self._options.django_debug:
|
||||
|
|
|
|||
|
|
@ -714,6 +714,8 @@ class PyDB(object):
|
|||
# DAP related
|
||||
self._dap_messages_listeners = []
|
||||
|
||||
self.multi_thread = False
|
||||
|
||||
if set_as_global:
|
||||
# Set as the global instance only after it's initialized.
|
||||
set_global_debugger(self)
|
||||
|
|
@ -1842,6 +1844,10 @@ class PyDB(object):
|
|||
|
||||
return eb
|
||||
|
||||
def set_multi_thread(self, multi_thread):
|
||||
self.multi_thread = multi_thread
|
||||
self.cmd_factory.set_multi_thread(multi_thread)
|
||||
|
||||
def set_suspend(self, thread, stop_reason, suspend_other_threads=False, is_pause=False, original_step_cmd=-1):
|
||||
'''
|
||||
:param thread:
|
||||
|
|
@ -1882,7 +1888,7 @@ class PyDB(object):
|
|||
info.conditional_breakpoint_exception = None
|
||||
self._send_breakpoint_condition_exception(thread, conditional_breakpoint_exception_tuple)
|
||||
|
||||
if not suspend_other_threads and self.multi_threads_single_notification:
|
||||
if not self.multi_thread and not suspend_other_threads and self.multi_threads_single_notification:
|
||||
# In the mode which gives a single notification when all threads are
|
||||
# stopped, stop all threads whenever a set_suspend is issued.
|
||||
suspend_other_threads = True
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ class Client(components.Component):
|
|||
},
|
||||
)
|
||||
sessions.report_sockets()
|
||||
self.multi_thread = False
|
||||
|
||||
def propagate_after_start(self, event):
|
||||
# pydevd starts sending events as soon as we connect, but the client doesn't
|
||||
|
|
@ -228,6 +229,7 @@ class Client(components.Component):
|
|||
self._initialize_request = None
|
||||
|
||||
arguments = request.arguments
|
||||
self.multi_thread = arguments.get('multiThread', False)
|
||||
if self.launcher:
|
||||
redirecting = arguments.get("console") == "internalConsole"
|
||||
if "RedirectOutput" in debug_options:
|
||||
|
|
@ -629,12 +631,14 @@ class Client(components.Component):
|
|||
|
||||
@message_handler
|
||||
def pause_request(self, request):
|
||||
request.arguments["threadId"] = "*"
|
||||
if not self.multi_thread:
|
||||
request.arguments["threadId"] = "*"
|
||||
return self.server.channel.delegate(request)
|
||||
|
||||
@message_handler
|
||||
def continue_request(self, request):
|
||||
request.arguments["threadId"] = "*"
|
||||
if not self.multi_thread:
|
||||
request.arguments["threadId"] = "*"
|
||||
|
||||
try:
|
||||
return self.server.channel.delegate(request)
|
||||
|
|
@ -642,7 +646,7 @@ class Client(components.Component):
|
|||
# pydevd can sometimes allow the debuggee to exit before the queued
|
||||
# "continue" response gets sent. Thus, a failed "continue" response
|
||||
# indicating that the server disconnected should be treated as success.
|
||||
return {"allThreadsContinued": True}
|
||||
return {"allThreadsContinued": not self.multi_thread}
|
||||
|
||||
@message_handler
|
||||
def debugpySystemInfo_request(self, request):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue