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, 这个很简单,就不贴代码了.


相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
2天前
|
存储 Linux Shell
python移除/删除非空文件夹/目录的最有效方法是什么?
python移除/删除非空文件夹/目录的最有效方法是什么?
8 0
|
5天前
|
Python
【Python 基础】Python中的实例方法、静态方法和类方法有什么区别?
【5月更文挑战第6天】【Python 基础】Python中的实例方法、静态方法和类方法有什么区别?
|
5天前
|
数据处理 Python
Python中每个字段增加多条数据的高效方法
Python中每个字段增加多条数据的高效方法
11 1
|
5天前
|
存储 数据挖掘 Python
Python技术分享:实现选择文件或目录路径的方法
Python技术分享:实现选择文件或目录路径的方法
15 2
|
5天前
|
数据处理 Python
Python中按指定数量分割列表字符串的方法
Python中按指定数量分割列表字符串的方法
9 1
|
9天前
|
Python
使用Python pandas的sort_values()方法可按一个或多个列对DataFrame排序
使用Python pandas的sort_values()方法可按一个或多个列对DataFrame排序。示例代码展示了如何按&#39;Name&#39;和&#39;Age&#39;列排序 DataFrame。先按&#39;Name&#39;排序,再按&#39;Age&#39;排序。sort_values()的by参数接受列名列表,ascending参数控制排序顺序(默认升序),inplace参数决定是否直接修改原DataFrame。
23 1
|
11天前
|
机器学习/深度学习 数据可视化 前端开发
【Python机器学习专栏】机器学习模型评估的实用方法
【4月更文挑战第30天】本文介绍了机器学习模型评估的关键方法,包括评估指标(如准确率、精确率、召回率、F1分数、MSE、RMSE、MAE及ROC曲线)和交叉验证技术(如K折交叉验证、留一交叉验证、自助法)。混淆矩阵提供了一种可视化分类模型性能的方式,而Python的scikit-learn库则方便实现这些评估。选择适合的指标和验证方法能有效优化模型性能。
|
11天前
|
机器学习/深度学习 算法 Python
【Python机器学习专栏】Python中的特征选择方法
【4月更文挑战第30天】本文介绍了机器学习中特征选择的重要性,包括提高模型性能、减少计算成本和增强可解释性。特征选择方法主要包括过滤法(如相关系数、卡方检验和互信息)、包装法(如递归特征消除和顺序特征选择)和嵌入法(如L1正则化和决策树)。在Python中,可利用`sklearn`库的`feature_selection`模块实现这些方法。通过有效的特征选择,能构建更优的模型并深入理解数据。
|
11天前
|
机器学习/深度学习 数据采集 数据可视化
【Python 机器学习专栏】数据缺失值处理与插补方法
【4月更文挑战第30天】本文探讨了Python中处理数据缺失值的方法。缺失值影响数据分析和模型训练,可能导致模型偏差、准确性降低和干扰分析。检测缺失值可使用Pandas的`isnull()`和`notnull()`,或通过可视化。处理方法包括删除含缺失值的行/列及填充:固定值、均值/中位数、众数或最近邻。Scikit-learn提供了SimpleImputer和IterativeImputer类进行插补。选择方法要考虑数据特点、缺失值比例和模型需求。注意过度插补和验证评估。处理缺失值是提升数据质量和模型准确性关键步骤。
|
11天前
|
开发者 Python
【亮剑】Python中`List.append()`方法不起作用的原因及解决方案
【4月更文挑战第30天】本文探讨了Python中`List.append()`方法不起作用的原因及解决方案。问题可能源于变量作用域、可变类型与不可变类型混淆、函数内的局部变量、错误的赋值操作或并发修改。解决方法包括检查变量、确保正确操作列表、使用`global`关键字、避免错误赋值和采用同步机制处理并发问题。理解这些问题对于编写健壮的Python代码至关重要。