资讯专栏INFORMATION COLUMN

Python 调试方法

klivitamJ / 1476人阅读

摘要:背景这几天一直在查一个线上程序住的问题这个程序总是在运行分钟后住通过以下的一些调试手段发现是打日志的时候因为满被了日志是默认打到的无论日志级别而我这个程序是被另一个程序调起的父进程没有接收子进程的导致了被打满在调试的过程中用到了以下几种调试

FROM http://kamushin.github.io/debug/python.html

背景

这几天一直在查一个线上程序 hang 住的问题. 这个程序总是在运行50分钟后 hang 住, 通过以下的一些调试手段,发现是打日志的时候因为 buffer 满被 block 了.
Python 日志是默认打到 stderr 的, 无论日志级别. 而我这个程序是被另一个程序调起的, 父进程没有接收子进程的 stderr, 导致了 buffer 被打满.
在调试的过程中, 用到了以下几种 Python 调试手段, 于是记录以下.

GDB

GDB是一个广为人知的调试器, 而且线上可用, 非常赞. 但是默认配置的 GDB 并不能打印 Python 当前调用栈. 我们需要对其做些配置.
首先进行gdb的安装, 需要gdb7以上版本
sudo yum install gdb python-debuginfo
然后下载这份 gdb 配置文件 http://svn.python.org/projects/python/trunk/Misc/gdbinit ~/.gdbinit
对于一个线上已经hang住的程序来说, 可以用 gdb -p pid 的形式进行 attach, 打印出当前调用栈.
一般来说, 必须是带debug symbol的Python 编译版本才能打印出足够多的信息, 但是线上的 Python 版本往往是不带debug symbol 的, 于是我们要修改下上述的配置文件

    <<<<         if $pc > PyEval_EvalFrameEx && $pc < PyEval_EvalCodeEx
    >>>>         if $pc > PyEval_EvalFrameEx && $pc < PyEval_EvalCodeEx && $fp != 0

~/.gdbinit 进行上述修改, 即可成功打印出当前 hang住进程的调用栈.
具体到我这次遇到的问题, 在打出调用栈后发现是卡死在 log 模块的 emit 上, 于是 strace 下看到果然是卡死在 write 的系统调用上, 顺利找到了原因.
更多的用法可以看https://wiki.python.org/moin/DebuggingWithGdb, 不过大部分的用法依然需要debug symbol, 按照 wiki 来,不一定可以顺利实现.

PDB

PDB是 Python 自带的一个调试模块. 可以以python -m pdf xxx.py 的形式, 以调试模式启动一个 Python 进程.
虽然似乎不能 attach 到已运行的进程上, 但是提供了一个简单快速的调试方式.

Singal AND InteractiveConsole

上述的方式都是不需要侵入代码的, 这里再提供一种侵入代码的方式.

import code, traceback, signal

def debug(sig, frame):
    """Interrupt running process, and provide a python prompt for
    interactive debugging."""
    d={"_frame":frame}         # Allow access to frame object.
    d.update(frame.f_globals)  # Unless shadowed by global
    d.update(frame.f_locals)

    i = code.InteractiveConsole(d)
    message  = "Signal received : entering python shell.
Traceback:
"
    message += "".join(traceback.format_stack(frame))
    i.interact(message)

def listen():
    signal.signal(signal.SIGUSR1, debug)  # Register handler

基本原理是给SIGUSR1信号加上一个handler, handler 执行时会把当前的变量加载到一个交互式窗口, 然后开启交互式console, 接下来就像打开一个 REPL 一样了, 可以查看当前的变量值, 可以改变变量值, 可以调用函数看看结果是什么, 查看完后^d离开, 就可以让程序继续执行下去.
在加好 handler 后, 我们可以用os.kill(pid, signal.SIGUSR1)的方式, 调起 handler, 进行调试.
值得注意的是, 由于和console 的交互需要 stdout 的支持, 而父子进程默认是不共享 stdout 的,所以当要调试子进程的时候, 需要重定向子进程的 stdout 到父进程的 stdout, 这个很简单,就不贴代码了.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/37948.html

相关文章

  • 调试和分析Python脚本

    摘要:调试器可帮助程序员分析完整的代码。我们将使用标准库中的模块调试我们的脚本。例外是程序执行期间发生的错误。设置断点并检查堆栈帧,并列出源代码。输入以继续调试。分析和计时程序分析程序意味着测量程序的执行时间。的模块用于分析程序。 showImg(https://segmentfault.com/img/remote/1460000018807029?w=902&h=442); 来源 | ...

    wenzi 评论0 收藏0
  • Python 开发工具集:关于文档、测试、调试、程序的优化和分析

    摘要:通过单元测试,开发者可以为构成程序的每一个元素例如,独立的函数,方法,类以及模块编写一系列独立的测试用例。在每个测试中,断言可以用来对不同的条件进行检查。当退出调试器时,调试器会自动恢复程序的执行。 Python已经演化出了一个广泛的生态系统,该生态系统能够让Python程序员的生活变得更加简单,减少他们重复造轮的工作。同样的理念也适用于工具开发者的工作,即便他们开发出的工具并没有出现...

    shenhualong 评论0 收藏0
  • vscode的python环境配置

    摘要:安装一个的版本。选一个的解释器想要在中运行代码必须要告诉使用哪个解释器才行。运行文件最简单的方法是点击右键选择在终端运行。然后选择调试工具栏上的设置图标。该命令打开一个可用调试器的菜单,显示和。选择,这个会创建一个包含许多配置的文件。 开始 安装 Python extension 。 安装一个python3的版本。 选一个python的解释器 想要在vscode中运行python代...

    vpants 评论0 收藏0
  • Python:Tornado 第四章:Tornado网站部署:第一节:调试模式

    摘要:上一篇文章第三章概念及应用第三节客户端编程下一篇文章第四章网站部署第二节静态文件之前着重讲解的编程知识点,所有之前的例子都使用最简单的启动方式运行。 上一篇文章:Python:Tornado 第三章:HTML5 WebSocket概念及应用:第三节:客户端编程下一篇文章:Python:Tornado 第四章:Tornado网站部署:第二节:静态文件 之前着重讲解Tornado的编程知...

    desdik 评论0 收藏0
  • 使用Visual Studio Code编写调试Python Flask程序

    摘要:最近由于工作关系,开始写程序,同事有用的,有用的。第一种适合及以上版本,因为在版本起,出现了文件,配置可以写成用文件启动程序,实测可以触发断点。 最近由于工作关系,开始写flask web程序,同事有用Vim的,有用PyCharm的。在调研了一通python的编辑器,IDE之后,发现Visual Studio Code相对比较适合我。 VSC有相对较全的功能,比较好看舒服的主题,良好的...

    gotham 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<