首页 > 解决方案 > sys.settrace 似乎在线程中被忽略

问题描述

所以我开始研究一个实验性的调试器,试图解决我遇到的一些问题,因为我主要调试位于远程机器上的东西。

这是我拥有的代码示例:

debugger = threading.local()
debugger.instance = None
        
_logger = logging.getLogger(__name__)

class Debugger(object):
    ...
    def __call__(self, frame, event, arg):
        thread_name = threading.current_thread().getName()
        _logger.debug(
            "Start Trace (%s) %s %s",
            thread_name,
            event,
            arg
        )
        ...
    


def set_trace():
    _logger.debug("[%s] Set trace", threading.current_thread().getName())
    thread_name = threading.current_thread().getName()

    try:
        instance = debugger.instance
        if not instance:
            debugger.instance = Debugger()
            instance = debugger.instance
    except Exception:
        debugger.instance = Debugger()
        instance = debugger.instance

    def destroy(instance):
        def wrap():
            _logger.debug("[%s] Destroying debugger instance", thread_name)
            instance.stop()
        return wrap

    atexit.register(destroy(instance))

    if not instance.stopped:
        _logger.debug("[%s] Set trace to %s", thread_name, instance)
        sys.settrace(instance)
    else:
        _logger.debug("[%s] Remove trace", thread_name)
        sys.settrace(None)

以及程序的代码:

import sys
from threading import Thread
from threading import get_ident
import logging

logging.basicConfig(level='DEBUG')

value = 2

def start_job():
    print("In thread", get_ident())
    import d2
    d2.set_trace()
    # breakpoint()                                                                                                                                                                                                                            
    print("In thread value", value)
    print("End thread", get_ident())

thread = Thread(target=start_job, args=tuple())
thread.start()

这是我得到的日志:

....
In thread 139757304022784
DEBUG:d2:[Thread-1] Set trace
DEBUG:d2:[Thread-1] Waiting for the client from the async thread
DEBUG:d2:[Thread-1] Set trace to <d2.Debugger object at 0x7f1bc877fe80>
In thread value 2
End thread 139757304022784
....

此日志中的第一行来自 set_trace,第二行来自调试器的构造函数。第三行就在 sys.settrace 设置调试器之前。在线程中,值 2 是 set_trace 调用之后的行,最后一行几乎是线程本身的最后一行。

问题是,对于 MainThread,它可以完美运行,我实际上可以看到如下日志:

DEBUG:d2:Start Trace (MainThread) call None
DEBUG:d2:Start Trace (MainThread) line None
DEBUG:d2:Start Trace (MainThread) line None
DEBUG:d2:Start Trace (MainThread) call None
DEBUG:d2:Start Trace (MainThread) line None

但是对 settrace 的调用似乎完全被忽略了。我也尝试使用threading.settrace它似乎或多或少地工作。它将开始调试线程,但实际上我不想跟踪整个线程,而只想跟踪我放置断点的地方。

最终,我希望能够sys.settrace(None)禁用跟踪并在需要时让断点启用调试器。

编辑:我想我在这个问题中找到了这种行为的原因。“sys.settrace”是否在 Python 3.5 中正常工作,但在 Python 3.6 中不能正常工作?

每当输入新的本地范围时,都会调用跟踪函数(事件设置为“调用”);它应该返回对要用于新范围的本地跟踪函数的引用,如果不应该跟踪范围,则返回 None 。

显然,它不会在 settrace 之后立即开始跟踪,而是在调用 settrace() 后创建新的本地作用域之后才开始跟踪。

标签: pythonmultithreadingdebugging

解决方案


推荐阅读