debugging 如何让PDB识别源代码在运行之间发生了变化?

doinxwow  于 2023-10-24  发布在  其他
关注(0)|答案(7)|浏览(168)

从我所知道的来看,PDB不能识别源代码在“运行”之间的变化。也就是说,如果我在调试,注意到一个bug,修复那个bug,并在PDB中重新编译程序(即不退出PDB),PDB将不会重新编译代码。我仍然会调试旧版本的代码,即使PDB列出了新的源代码。
那么,当源代码发生变化时,PDB不会更新编译后的代码吗?如果不是,有没有办法让它这样做?我希望能够保持在一个单独的PDB会话中,以保持我的断点等。
FWIW,gdb会注意到它正在调试的程序在它下面发生的变化,尽管只有在程序重新启动时。这是我试图在pdb中复制的行为。

gk7wooem

gk7wooem1#

下面的小模块可能会有所帮助。如果您在PDB会话中导入它,则可以用途:

pdb> pdbs.r()

强制重新加载除main以外的所有非系统模块。代码跳过了这一步,因为它抛出了一个ImportError('Cannot re-init internal module**main*')异常。

# pdbs.py - PDB support

from __future__ import print_function

def r():
    """Reload all non-system modules, to reload stuff on pbd restart. """
    import importlib
    import sys

    # This is likely to be OS-specific
    SYS_PREFIX = '/usr/lib'

    for k, v in list(sys.modules.items()):
        if (
            k == "__main__" or
            k.startswith("pdb") or
            not getattr(v, "__file__", None)
            or v.__file__.startswith(SYS_PREFIX)
        ):
            continue
        print("reloading %s [%s]" % (k, v.__file__), file=sys.stderr)
        importlib.reload(v)
wlwcrazw

wlwcrazw2#

基于@pourhaus answer(从2014年开始),这个配方用reload命令增强了 * pdb++调试器 *(预计在任何Python安装的Linux和Windows上都可以工作)。
提示:新的reload命令接受一个可选的模块前缀列表来重新加载(和 exclude),而不是在恢复调试时中断已经加载的 globals
只需将以下 Python-3.6 代码插入到~/.pdbrc.py文件中:

## Augment `pdb++` with a `reload` command
#
#  See https://stackoverflow.com/questions/724924/how-to-make-pdb-recognize-that-the-source-has-changed-between-runs/64194585#64194585

from pdb import Pdb

def _pdb_reload(pdb, modules):
    """
    Reload all non system/__main__ modules, without restarting debugger.

    SYNTAX:
        reload [<reload-module>, ...] [-x [<exclude-module>, ...]]

    * a dot(`.`) matches current frame's module `__name__`;
    * given modules are matched by prefix;
    * any <exclude-modules> are applied over any <reload-modules>.

    EXAMPLES:
        (Pdb++) reload                  # reload everything (brittle!)
        (Pdb++) reload  myapp.utils     # reload just `myapp.utils`
        (Pdb++) reload  myapp  -x .     # reload `myapp` BUT current module

    """
    import importlib
    import sys

    ## Derive sys-lib path prefix.
    #
    SYS_PREFIX = importlib.__file__
    SYS_PREFIX = SYS_PREFIX[: SYS_PREFIX.index("importlib")]

    ## Parse args to decide prefixes to Include/Exclude.
    #
    has_excludes = False
    to_include = set()
    # Default prefixes to Exclude, or `pdb++` will break.
    to_exclude = {"__main__", "pdb", "fancycompleter", "pygments", "pyrepl"}
    for m in modules.split():
        if m == "-x":
            has_excludes = True
            continue

        if m == ".":
            m = pdb._getval("__name__")

        if has_excludes:
            to_exclude.add(m)
        else:
            to_include.add(m)

    to_reload = [
        (k, v)
        for k, v in sys.modules.items()
        if (not to_include or any(k.startswith(i) for i in to_include))
        and not any(k.startswith(i) for i in to_exclude)
        and getattr(v, "__file__", None)
        and not v.__file__.startswith(SYS_PREFIX)
    ]
    print(
        f"PDB-reloading {len(to_reload)} modules:",
        *[f"  +--{k:28s}:{getattr(v, '__file__', '')}" for k, v in to_reload],
        sep="\n",
        file=sys.stderr,
    )

    for k, v in to_reload:
        try:
            importlib.reload(v)
        except Exception as ex:
            print(
                f"Failed to PDB-reload module: {k} ({v.__file__}) due to: {ex!r}",
                file=sys.stderr,
            )

Pdb.do_reload = _pdb_reload
lawou6xi

lawou6xi3#

如果你已经导入了一个模块,Python不会重新读取它,除非你明确要求这样做,即使用reload(module)。然而,reload远不是防弹的(另一种策略见xreload)。
在Python代码重载中有很多陷阱。为了更健壮地解决问题,您可以使用一个类 Package PDB,该类将断点信息记录到磁盘上的文件中,并在命令下播放它们。
(对不起,忽略这个答案的第一个版本;现在还为时过早,我没有仔细阅读你的问题。

goucqfw6

goucqfw64#

我决定在输入脚本中注解一些行,

(Pdb) run

我让pdb认识到了这个变化。坏的方面:它从一开始就运行脚本。下面是好的方面。

(Pdb) help run
run [args...]
        Restart the debugged python program. If a string is supplied
        it is split with "shlex", and the result is used as the new
        sys.argv.  History, breakpoints, actions and debugger options
        are preserved.  "restart" is an alias for "run".
y53ybaqx

y53ybaqx5#

可能不适用于更复杂的程序,但对于使用Python v3.5.3使用importlib.reload()的简单示例:

[user@machine ~] cat test.py
print('Test Message')

#
# start and run with debugger
#
[user@machine ~] python3 -m pdb test.py
> /home/user/test.py(1)<module>()
-> print('Test Message')
(Pdb) c
Test Message
The program finished and will be restarted
> /home/user/test.py(1)<module>()
-> print('Test Message')

#
# in another terminal, change test.py to say "Changed Test Message"
#

#
# back in PDB:
#
(Pdb) import importlib; import test; importlib.reload(test)
Changed Test Message
<module 'test' from '/home/user/test.py'>
(Pdb) c
Test Message
The program finished and will be restarted
> /home/user/test.py(1)<module>()
-> print('Changed Test Message')
(Pdb) c
Changed Test Message
The program finished and will be restarted
> /home/user/test.py(1)<module>()
-> print('Changed Test Message')
ivqmmu1c

ivqmmu1c6#

我是Python的新手,但在阅读了其他答案后,我对这个问题有了一个想法,如果你“注意到一个bug,修复那个bug,并在PDB中重新编译程序(即不退出PDB),”那么您希望PDB继续使用的状态(断点、命令等)的作用也不会很好,特别是因为行号可能已经更改,与断点相关的任何逻辑以及它们使用的条件都可能被破坏。
对于那些你不想重复输入的有用的调试命令,文档说“如果一个文件.pdbrc存在于用户的主目录或当前目录中,它将以'utf-8'编码读取并执行,就像它在调试器提示符下输入一样。”
这里要解决的最后一个大问题是如何在PDB中保留有用的commands命令,因为alias没有指定如何别名多行PDB命令(我只知道commands)。

zvms9eto

zvms9eto7#

ipdb %autoreload扩展名

6.2.0 docs文档http://ipython.readthedocs.io/en/stable/config/extensions/autoreload.html#module-IPython.extensions.autoreload:

In [1]: %load_ext autoreload

In [2]: %autoreload 2

In [3]: from foo import some_function

In [4]: some_function()
Out[4]: 42

In [5]: # open foo.py in an editor and change some_function to return 43

In [6]: some_function()
Out[6]: 43

相关问题