不能断点调试python_为Python调试构建一个不间断的断点
不能断点调试python
这是Rookout团队如何为Python建立不间断断点的故事,以及我们在此过程中学到的一些经验教训。 我将在本月于旧金山的PyBay 2019上展示有关Python调试的所有细节 。 让我们深入。
Python调试的核心:sys.set_trace
有很多Python调试器。 一些更受欢迎的包括:
- pdb ,Python标准库的一部分
- PyDev ,Eclipse和PyCharm IDE背后的调试器
- ipdb ,IPython调试器
尽管有很多选择,但是几乎每个Python调试器都仅基于一个函数: sys.set_trace 。 让我告诉你, sys.settrace可能只是Python标准库中最复杂的函数。
简单来说, settrace为解释器注册一个跟踪函数,在以下任何一种情况下都可以调用该函数:
- 函数调用
- 行执行
- 函数返回
- 引发异常
一个简单的跟踪函数可能看起来像这样:
def simple_tracer ( frame , event , arg ) :
co = frame. f_code
func_name = co. co_name
line_no = frame. f_lineno
print ( "{e} {f} {l}" . format (
e = event , f = func_name , l = line_no ) )
return simple_tracer
在查看此函数时,首先想到的是其参数和返回值。 跟踪函数参数为:
框架对象,是函数执行时解释器的完整状态
事件字符串,可以是call , line , return或exception
arg对象,它是可选的,取决于事件类型
跟踪函数返回自身是因为解释器跟踪两种跟踪函数:
全局跟踪函数(每个线程):该跟踪函数由sys.settrace为当前线程设置,并且在解释器创建新框架时调用(基本上在每个函数调用上)。 虽然没有记录的方法来为其他线程设置跟踪功能,但是您可以调用threading.settrace来为所有新创建的线程模块线程设置跟踪功能。
局部跟踪函数(每帧):解释器将此跟踪函数设置为在创建帧时全局跟踪函数返回的值。 创建框架后,没有任何记录的方法来设置本地跟踪功能。
此机制旨在使调试器可以更精确地控制要跟踪的帧,以减少对性能的影响。
只需三个简单步骤即可构建调试器(或者我们认为如此)
在所有这些背景下,使用自定义跟踪功能编写自己的调试器看起来像一项艰巨的任务。 幸运的是,标准的Python调试器pdb是在Bdb (构建调试器的基类)的基础上构建的。
一个基于Bdb的幼稚断点调试器可能看起来像这样:
import bdb
import inspect
class Debugger ( bdb. Bdb ) :
def __init__ ( self ) :
Bdb. __init__ ( self )
self . breakpoints = dict ( )
self . set_trace ( )
def set_breakpoint ( self , filename , lineno , method ) :
self . set_break ( filename , lineno )
try :
self . breakpoints [ ( filename , lineno ) ] . add ( method )
except KeyError :
self . breakpoints [ ( filename , lineno ) ] = [ method ]
def user_line ( self , frame ) :
if not self . break_here ( frame ) :
return
# Get filename and lineno from frame
( filename , lineno , _ , _ , _ ) = inspect . getframeinfo ( frame )
methods = self . breakpoints [ ( filename , lineno ) ]
for method in methods:
method ( frame )
所有这些是:
从Bdb继承,并编写一个简单的构造函数来初始化基类并进行跟踪。
添加使用Bdb设置断点并跟踪我们的断点的set_breakpoint方法。
覆盖Bdb在某些用户行上调用的user_line方法。 该函数确保正在为断点调用该函数,获取源位置并调用已注册的断点
简单的Bdb调试器工作得如何?
淘汰计划旨在将类似调试器的用户体验引入生产级性能和用例。 那么,我们的幼稚断点调试器的性能如何?
为了测试它并衡量整体性能开销,我们编写了两种简单的测试方法,并在多种情况下分别执行了1600万次。 请记住,在任何情况下都没有执行断点。
def empty_method ( ) :
pass
def simple_method ( ) :
a = 1
b = 2
c = 3
d = 4
e = 5
f = 6
g = 7
h = 8
i = 9
j = 10
使用调试器需要花费大量时间才能完成。 糟糕的结果清楚地表明,我们幼稚的Bdb调试器尚未投入生产。
优化调试器
减少调试器开销的主要方法有以下三种:
尽可能限制本地跟踪:与全局跟踪相比,本地跟踪的成本很高,因为每行代码的事件数量更多。
优化“呼叫”事件并将控制权更快地返回到解释器: 呼叫事件的主要工作是确定是否跟踪。
优化“行”事件并将控制权更快地返回到解释器: 在线事件的主要工作是确定是否达到断点。
因此,我们分叉了Bdb ,减少了功能集,简化了代码,针对热代码路径进行了优化,并获得了令人印象深刻的结果。 但是,我们仍然不满意。 因此,我们对其进行了另一次测试,将代码迁移并优化为.pyx ,然后使用Cython对其进行了编译 。 最终结果(如下所示)仍然不够好。 因此,我们最终深入研究了CPython的源代码,并意识到我们无法使跟踪足够快地用于生产。
拒绝Bdb以支持字节码操作
在最初对标准调试方法的反复试验感到失望之后,我们决定研究一种不太明显的选择:字节码操作。
Python解释器的工作主要分为两个阶段:
将Python源代码编译成Python字节码:这种不可读(对人类而言)的格式已针对有效执行进行了优化,并且经常缓存在我们都喜欢的.pyc文件中。
在解释器循环中迭代字节码:一次执行一条指令。
这是我们选择的模式:使用字节码操作来设置无中断断点而没有全局开销。 这是通过在内存中找到代表我们感兴趣的源代码行的字节码并在相关指令之前插入一个函数调用来完成的。 这样,解释器不必做任何额外的工作来支持我们的断点。
这种方法不是魔术。 这是一个简单的例子。
我们从一个非常简单的函数开始:
def multiply ( a , b ) :
result = a * b
return result
在检查模块(具有几个有用的实用程序)中隐藏的文档中,我们了解到可以通过访问multiple.func_code.co_code来获取函数的字节码:
'| \x 00 \x 00| \x 01 \x 00 \x 14} \x 02 \x 00| \x 02 \x 00S'
可以使用Python标准库中的dis模块来改进此不可读的字符串。 通过调用dis.dis(multiply.func_code.co_code) ,我们得到:
4 0 LOAD_FAST 0 ( a )
3 LOAD_FAST 1 ( b )
6 BINARY_MULTIPLY
7 STORE_FAST 2 ( result )
5 10 LOAD_FAST 2 ( result )
13 RETURN_VALUE
这使我们更接近了解调试幕后发生的事情,而不是直接解决方案。 不幸的是,Python没有提供从解释器内部更改函数的字节码的方法。 您可以覆盖函数对象,但这对于大多数实际调试场景来说还不够。 您必须使用本机扩展以环形方式进行处理。
结论
在构建新工具时,您总是最终会学到很多东西的工作原理。 这也使您能够开箱即用,并为意外的解决方案保持头脑开放。
在Rookout的不中断断点上工作使我了解了很多有关编译器,调试器,服务器框架,并发模型的知识。 如果您想了解有关字节码操作的更多信息,则Google的开源cloud-debug-python提供了用于编辑字节码的工具。
Liran Haimovitch将在8月17日至18日在旧金山举行的PyBay上展示“ 了解Python的调试内部 原理 ”。 购买机票时,使用代码OpenSource35可以打折,让他们知道您从我们的社区中了解到了该活动。
翻译自: https://opensource.com/article/19/8/debug-python
不能断点调试python
不能断点调试python_为Python调试构建一个不间断的断点相关推荐
- python断点调试_「Python调试器」,快速定位各种疑难杂症!!!
在很多的编辑器其实都带着「调试程序」的功能,比如写 c/c++ 的 codeblocks,写 Python 的 pycharm,这种图形界面的使用和显示都相当友好,简单方便易学,这个不是我这篇文章要讲 ...
- 文件不能断点 webstorm_详解python使用金山词霸的翻译功能(调试工具断点的使用)...
这篇文章主要介绍了详解python使用金山词霸的翻译功能(调试工具断点的使用),本文给大家介绍得非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下 今天试着用python获取金山 ...
- 利用 50 行 Python 代码构建一个在线文本生成器!
本指南将引导你学习构建一个自动完成任意输入文本的Web应用程序. 作者 | Dev Sharma 译者 | 苏本如,责编 | 郭芮 出品 | CSDN(ID:CSDNnews) 以下为译文: 在本文中 ...
- python 按钮更改输入框的值_利用 50 行 Python 代码构建一个在线文本生成器!
本指南将引导你学习构建一个自动完成任意输入文本的Web应用程序. 作者 | Dev Sharma译者 | 苏本如,责编 | 郭芮出品 | CSDN(ID:CSDNnews) 以下为译文: 在本文中,我 ...
- 用python来构建一个word文档-写文章
直接使用word文档已经难不倒大家了,有没有想过用python构建一个word文档写点文章呢?当然这个文章的框架需要我们用代码一点点的建立,在过程上有一点繁琐,一下子看不懂的小伙伴可以把它拆分成几个部 ...
- 几十行python代码构建一个前后端分离的目标检测演示网站,代码开源
在深度学习更讲究实用和落地的今天,构建一个简单的,可以利用浏览器和后端交互的演示性 Demo 可以说非常重要且实用了.本文我们将简单的介绍如何用几十行核心代码构建一个好用的.前后端分离的Demo. 2 ...
- python开发框架 代码生成_500 行 Python 代码构建一个轻量级爬虫框架
转载:https://www.jqhtml.com/11084.html 既然已经有像 Scrapy 这样优秀的爬虫框架,为何还要造轮子呢?嗯,其实最主要的还是想要将学习到 Python 知识综合起来 ...
- 猜数字游戏缩小范围python_【python小白】一个猜数字的小游戏
[失败和犯规] [成功] 源码:#一个猜数字的游戏a,b=0,15 print('这是一个猜数字的游戏,范围在%d-%d,总共9次机会,每猜三次范围减小10'%(a,b)) import random ...
- Python 调试器入门
Python 生态系统包含丰富的工具和库,可以让开发人员更加舒适. 例如,我们之前已经介绍了如何使用交互式 shell 增强 Python.本文重点介绍另一种可以节省时间并提高 Python 技能的工 ...
最新文章
- 学校的计算机教室火灾级别,学校教室火灾隐患有哪些
- python官网安装步骤-新手Windows下Python下载安装教程及配置注意事项
- css 书写记录(兼容性)
- 安装SAP Business One对软硬件有哪些要求
- 【Python基础】python使用PyPDF2和pdfplumber操作pdf
- [CF544D]Destroying Roads_最短路_bfs
- 20162317 2017-2018-1 《程序设计与数据结构》第8周学习总结
- php正规则表达式学习笔记(几个常用函数的区别)
- R plot图片背景设置为透明_学习健明老师发布的R语言练习题的学习笔记(一)...
- Java Maven项目打包成可执行jar文件
- SQL.H 通过此文件寻找sqlAPI编程的一种捷径
- Python 中星号的本质及其使用方式
- MathType requires a newer version of MT Extra等MathType问题的不兼容性解决方案
- [USACO09JAN]安全出行Safe Travel
- python机器学习乳腺癌细胞模型
- Arcgis土地利用转移矩阵制作
- 人工智能行业每日必读(02·04)
- iOS直播:评论框与粒子系统点赞动画
- 计算机界的传奇人物:高纳德
- 本地电脑ssh连接机器人,使用rviz控制机器人导航