【问题标题】:python: is it possible to attach a console into a running processpython:是否可以将控制台附加到正在运行的进程中
【发布时间】:2011-05-09 00:23:44
【问题描述】:

我只是想看看进程的状态,是否可以在进程中附加一个控制台,这样我就可以调用进程内部的函数并查看一些全局变量。

最好进程运行不受影响(当然性能会下降一点)

【问题讨论】:

标签: python


【解决方案1】:

如果您可以访问程序的源代码,则可以相对轻松地添加此功能。

Recipe 576515:Debugging a running python process by interrupting and providing an interactive prompt (Python)

引用:

这提供了允许任何 python 的代码 使用它的程序 在当前点中断,并且 通过普通 python 进行通信 交互式控制台。这允许 locals、globals 和相关程序 被调查的状态,以及 调用任意函数和 类。

要使用,进程应该导入 模块,并在任何时候调用listen() 在启动期间。打断这 进程,脚本可以运行 直接给出进程ID 进程作为参数进行调试。


rconsole 提供了大致相同概念的另一个实现。来自文档:

rconsole 是一个远程 Python 控制台 具有自动完成功能,可以是 用于检查和修改 正在运行的脚本的命名空间。

要在脚本中调用:

from rfoo.utils import rconsole
rconsole.spawn_server()

要从外壳附加:

$ rconsole

安全说明:rconsole 侦听器 以 spawn_server() 开始 接受任何本地连接,并且可以 因此在共享中使用是不安全的 托管或类似环境!

【讨论】:

  • 这真是一个非常好的食谱。使用管道和文件进行输入和输出非常聪明,我认为任何体面的项目都会从这种功能中受益。
  • 它非常不安全,所以请小心使用
  • 我尝试了配方,但它破坏了我的 python 安装。 “模块”对象没有属性“getmro”
  • 我编译了 cython 扩展,在将存根添加到代码并启动 rpc 服务器之后,但是 rconsole 总是会在任何操作时出现可耻的中断。 rfoo._rfoo.EofError: 0
【解决方案2】:

为什么不简单地使用pdb 模块呢?它允许您停止脚本、检查元素值并逐行执行代码。由于它是基于 Python 解释器构建的,它还提供了经典解释器提供的功能。要使用它,只需将这两行代码放入您希望停止并检查它的地方:

import pdb
pdb.set_trace()

【讨论】:

  • 你知道如何为给定线程 threading.enumerate() 启动它吗?
【解决方案3】:

这会中断您的进程(除非您在线程中启动它),但您可以使用code 模块来启动 Python 控制台:

import code
code.interact()

这将一直阻塞,直到用户通过执行exit() 退出交互式控制台。

code 模块至少在 Python v2.6 中可用,可能还有其他版本。

我倾向于将此方法与我的 Linux 工作的信号结合使用(对于 Windows,请参见下文)。我把这个放在我的 Python 脚本的顶部:

import code
import signal
signal.signal(signal.SIGUSR2, lambda sig, frame: code.interact())

然后从带有kill -SIGUSR2 <PID> 的shell 触发它,其中<PID> 是进程ID。然后该进程停止它正在执行的任何操作并显示一个控制台:

Python 2.6.2 (r262:71600, Oct  9 2009, 17:53:52)
[GCC 3.4.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

通常我会从那里加载远程调试器的服务器端组件,比如优秀的WinPDB

Windows 不是POSIX 兼容的操作系统,因此不提供与 Linux 相同的信号。但是,Python v2.2 and above expose a Windows-specific signal SIGBREAK(通过按 CTRL+Pause/Break 触发)。这不会干扰正常的CTRL+C (SIGINT) 操作,因此是一个方便的替代方案。

因此,上述的可移植但略显丑陋的版本是:

import code
import signal
signal.signal(
        vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR2"),
        lambda sig, frame: code.interact()
        )

这种方法的优点:

  • 没有外部模块(所有标准 Python 内容)
  • 在触发之前几乎不消耗任何资源(2x 导入)

这是我在生产环境中使用的代码,它将加载 WinPDB 的服务器端(如果可用)并回退到打开 Python 控制台。

# Break into a Python console upon SIGUSR1 (Linux) or SIGBREAK (Windows:
# CTRL+Pause/Break).  To be included in all production code, just in case.
def debug_signal_handler(signal, frame):
    del signal
    del frame

    try:
        import rpdb2
        print
        print
        print "Starting embedded RPDB2 debugger. Password is 'foobar'"
        print
        print
        rpdb2.start_embedded_debugger("foobar", True, True)
        rpdb2.setbreak(depth=1)
        return
    except StandardError:
        pass

    try:
        import code
        code.interact()
    except StandardError as ex:
        print "%r, returning to normal program flow" % ex

import signal
try:
    signal.signal(
            vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR1"),
            debug_signal_handler
            )
except ValueError:
    # Typically: ValueError: signal only works in main thread
    pass

【讨论】:

  • 您能否详细介绍一下在您拥有 Python 控制台后如何加载 WinPDB 的服务器端组件?
  • 我已将我的生产代码添加到答案中,但实际上您只需要在控制台中输入以下内容:''import rpdb2; rpdb2.start_embedded_debugger("foobar", True, True)'' 然后在提示时使用密码“foobar”启动 WinPDB GUI
【解决方案4】:

这里描述了另一种可能性,无需向 python 脚本添加内容:

https://wiki.python.org/moin/DebuggingWithGdb

不幸的是,这个解决方案还需要一些预先考虑,至少在某种程度上你需要使用带有调试符号的 python 版本。

【讨论】:

  • 对于大多数 Linux 发行版,python 是使用调试符号构建的,但调试符号位于不同的包中。调试符号包可以在你的 python 脚本已经启动后安装。
【解决方案5】:

使用pyrasite-shell。我不敢相信它工作得这么好,但确实如此。 “给它一个 pid,得到一个 shell”。

$ sudo pip install pyrasite
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope # If YAMA activated, see below.
$ pyrasite-shell 16262
Pyrasite Shell 2.0
Connected to 'python my_script.py'
Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.

>>> globals()
>>> print(db_session)
>>> run_some_local_function()
>>> some_existing_local_variable = 'new value'

这将启动 python shell 并访问正在运行的 python 进程的 globals() 和 locals() 变量,以及其他精彩的东西。

仅在 Ubuntu 上亲自测试过,但似乎也适合 OSX。

改编自this answer

注意:关闭ptrace_scope 属性的行仅对使用CONFIG_SECURITY_YAMA 构建的内核/系统是必需的。在敏感环境中小心弄乱 ptrace_scope,因为它可能会引入某些安全漏洞。详情请见here

【讨论】:

  • @Dirk 我相信 ptrace_scope 对于允许一个进程跟踪/与另一个不相关的进程交互是必不可少的。
  • 显然这仅在具有 YAMA 的系统上。建议编辑并删除我原来的评论。 (这个也要在一段时间内去。)
  • 在 Docker 中使用请参见此处:stackoverflow.com/questions/37072468/…
  • 2021 - 项目已死。未迁移到 python3 - 有轮子取决于 python2-dev PEP
【解决方案6】:

使用 PyCharm,我无法连接到 Ubuntu 中的进程。解决此问题的方法是禁用 YAMA。欲了解更多信息,请参阅askubuntu

echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

【讨论】:

    【解决方案7】:

    pdb_attach 非常适合我们将 Python 调试器附加到长时间运行的进程。

    作者是这样描述的: 这个包是为了回应调试长时间运行的进程的挫败感。将 pdb 附加到正在运行的 python 程序并查看发生了什么不是很好吗?这正是 pdb-attach 的作用。

    在你的主模块中进行如下设置:

    import pdb_attach
    pdb_attach.listen(50000)  # Listen on port 50000.
    

    当程序运行时,通过从命令行调用 pdb_attach 附加到它,并使用程序的 PID 和传递给pdb_attach.listen() 的端口:

    $ python -m pdb_attach <PID> 50000
    (Pdb)  # Interact with pdb as you normally would
    

    【讨论】:

      【解决方案8】:

      你可以使用我的项目madbg。它是一个 python 调试器,允许您附加到正在运行的 python 程序并在当前终端中调试它。它类似于pyrasitepyringe,但支持python3,不需要gdb,并使用IPython 作为调试器(这意味着pdb 带有颜色和自动完成功能)。

      例如,要查看脚本卡在哪里,您可以运行:

      madbg attach <pid>
      

      之后,您将拥有一个 pdb shell,您可以在其中调用函数和检查变量。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-17
        相关资源
        最近更新 更多