Python 调试方法

背景

这几天一直在查一个线上程序 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.\nTraceback:\n"
    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, 这个很简单,就不贴代码了.

时间: 2016-05-27

Python 调试方法的相关文章

总结的几个Python函数方法设计原则

  这篇文章主要介绍了总结的几个Python函数方法设计原则,本文讲解了每个函数只做一件事.保持简单.保持简短.输入使用参数.输出使用return语句等内容,需要的朋友可以参考下 在任何编程语言中,函数的应用主要出于以下两种情况: 1.代码块重复,这时候必须考虑用到函数,降低程序的冗余度 2.代码块复杂,这时候可以考虑用到函数,增强程序的可读性 当流程足够繁杂时,就要考虑函数,及如何将函数组合在一起.在Python中做函数设计,主要考虑到函数大小.聚合性.耦合性三个方面,这三者应该归结于规划与设

php程序调试方法总结

  相信很多朋友们都有调试程序的经历,然而很多时候调试程序是痛苦而又漫长的过程;它不仅需要细心,更需要耐心,切忌心浮气躁.但是当找出问题并顺利解决它时,又会给人无比激动的喜悦.这里总结一下笔者在程序调试中的使用的原则,工具,以及方法.这里需要说明的是,某些原则性的东西,各种语言都是差不多的,而涉及到具体的工具和某些具体的调试方法,这里只涉及web开发方面的内容. 总体原则: 1.找出问题原因: 程序需要调试,是因为程序有问题.而调试的第一目标是找到原因.常见调试方法, 排除法: 当我们面对整个复

VC++调试方法和技巧

便于调试的代码风格: 不用全局变量 所有变量都要初始化,成员变量在构造函数中初始化 尽量使用const 详尽的注释 VC++编译选项: 总是使用/W4警告级别 在调试版本里总是使用/GZ编译选项,用来发现在Release版本中才有的错误 没有警告的编译:保证在编译后没有任何警告,但是在消除警告前要进行仔细检查 调试方法: 1.使用 Assert(原则:尽量简单) assert只在debug下生效,release下不会被编译. 例子: char* strcpy(char* dest,char* s

思科路由器全局调试方法概述

使用思科路由器的用户非常多,关于思科路由器的全局调试今天在这里我为大家介绍一下,希望对大家有用. 在进行思科路由器配置时,全局和接口命令的界限是十分明显的.在这种情况下,我们使用全局来标识那些不能用于接口调试或者特定的传输介质类型和协议调试的命令. 在2500系列思科路由器中,就可以使用调试命令分析Cisco发现协议(Cisco Discovery Protocol,CDP).我们通过telnet远程登录到思科路由器.在缺省方式下,调试命令的输出被发送到控制台,如果处于telnet会话中,我们可

RC4文件加密的python实现方法

  本文实例讲述了RC4文件加密的python实现方法.分享给大家供大家参考.具体分析如下: 基于RC4流加密算法,使用扩展的16*16的S盒,32字节密钥. 目前应该是比较安全的. 刚学习python,好不容易调通了. 而且在VC和python下各实现了一遍,两个平台能够互相加解密,很有成就感的说. 下面是python3.0中的实现,在2.x下需要稍加修改. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

python绘图方法实例入门

  本文实例讲述了python绘图方法.分享给大家供大家参考.具体如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 # -*- coding:utf-8 -*- import matplotlib.pyplot as plt def main(): # 颜色列表 colorList = ['b','g','r','c','m','y','

python排序方法实例分析

  本文实例讲述了python排序方法.分享给大家供大家参考.具体如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 >>> def my_key1(x): ... return x % 10 ... >>> alist = [4, 5, 8, 1, 63, 8] >>> alist [4, 5, 8, 1, 63, 8] >>> alist.sor

新扬天、昭阳、VBKEM机型安装系统BIOS的调试方法

  故障现象: 新扬天.昭阳.VBKEM机型安装系统BIOS的调试方法以及安装系统时常见的问题 解决方案: 提示:以下操作可能会导致无法正常进入系统,建议您备份个人数据后在专业人士指导下操作. 1. 进入"BIOS",选择最后"Restart"的选项,先选择"OS Optimized Defaults"的选项敲"enter键(回车键)"改成"Disabled"的模式.然后选择"Load Defau

新消费YSZGN机型安装系统Bios的调试方法

  故障现象: 新消费YSZGN机型安装系统Bios的调试方法以及安装系统时常见的问题. 解决方案: 提示:以下操作可能会导致无法正常进入系统,建议您备份个人数据后在专业人士指导下操作. 1. 进入"bios",选择最后"Exit"的选项,先选择"OSOptimized Defaults"的选项敲"enter键(回车键)"改成"other os"的模式.然后选择"Load Default Sett