gpt4 book ai didi

python - 使用反汇编程序调试错误的Python

转载 作者:太空宇宙 更新时间:2023-11-04 03:37:13 27 4
gpt4 key购买 nike

阅读“每个程序员都应该知道的 97 件事”,我发现了一篇关于代码分析工具的有趣文章。

作者声称来自 Python 标准库的反汇编程序对于调试您的日常代码非常有用

这里是:“这个库(Python 标准库反汇编程序)可以反汇编的一件事是你的最后一个堆栈跟踪,给你关于哪个字节码指令抛出最后一个未捕获异常的准确反馈。”

但是书中并没有对此进行解释

那么有人知道上面的模块如何对调试有用吗?

最佳答案

虽然反汇编器可以作为一种工具来帮助您了解 Python 如何理解您编写的内容,但它并不是唯一的工具。还有其他工具也可以提供帮助。正如我们将看到的,其中一些可以协同工作。

所以这是一小段 Python:

def five():
return 5
print(five())

这里是使用我编写的跨平台反汇编器对其进行反汇编的一部分,它被称为 xdis :

# Python bytecode 3.4 (3310)
# Disassembled from Python 3.4.2 (default, May 17 2015, 22:17:04)
# [GCC 4.8.2]
# Timestamp in code: 1499405520 (2017-07-07 01:32:00)
# Source code size mod 2**32: 39 bytes

# Method Name: <module>
# Filename: five.py
# Argument count: 0
# Kw-only arguments: 0
# Number of locals: 0
# Stack size: 2
# Flags: 0x00000040 (NOFREE)
# First Line: 1
# Constants:
# 0: <code object five at 0x7f99dd4e88a0, file "five.py", line 1>
# 1: 'five'
# 2: None
# Names:
# 0: five
# 1: print
1:
LOAD_CONST 0 (<code object five at 0x7f99dd4e88a0, file "five.py", line 1>)
LOAD_CONST 1 ('five')
MAKE_FUNCTION 0 (0 positional, 0 name and default, 0 annotations)
STORE_NAME 0 (five)

3:
LOAD_NAME 1 (print)
LOAD_NAME 0 (five)
CALL_FUNCTION 0 (0 positional, 0 keyword pair)
CALL_FUNCTION 1 (1 positional, 0 keyword pair)
POP_TOP
LOAD_CONST 2 (None)
RETURN_VALUE
...

(这是 Python 3.4,其他版本的细节略有不同。)

首先要注意的是python认为这段代码来自一个路径名为five.py的文件。如果您碰巧重命名了文件而不是 python 代码,这可能会使 Python 感到困惑。或者文件名可能是 tmp/five.py 而不是你应该寻找它。此外,在 Python 版本 3 和更高版本中,有文件的大小(模数 2**32)作为检查文件系统上的 five.py 是否是与 Python 在编译文件时看到的相同。

请注意代码的开头:我们正在加载一个常量对象,它恰好是一个函数的代码!然后是函数名称,最后调用 MAKE_FUNCTION 并将其存储在名为 five 的变量中。

如果您习惯了 C++、Go 或 Java 等不执行此操作的编译语言,那么有点不寻常的是,该函数是在您运行程序时当场创建的。如果我的程序之前有另一条指令,而是:

x = five()  # five is hasn't been defined here!
def five(): ...

这会失败,因为 MAKE_FUNCTION 尚未运行,因此在开始时还没有定义五个。

现在我还建议您也可以使用调试器来学习它,我再次建议 trepan2trepan3 内置反汇编命令它们,甚至是该程序集的解析器。

另一个可以解释反汇编的地方是在 Python 对代码进行优化的极少数情况下。

考虑这个 Python 源代码:

if 1:
y = 5

在这里,在大约 2.3 之后的 Python 版本中,只会注意到 if 1: 是多余的,并删除该代码。但如果你改为说:

x = 1
if x:
y = 5

这足以混淆 Python 以保留测试。反汇编是我认为您可以知道这一点的唯一方法。

最后一个方面是,当您在调试器中停止或遇到错误时,准确了解您所处的位置。您经常(但不总是)得到错误的那一行,但有时这可能会造成混淆。普通的Python 屏蔽了此处有用的信息,即指令偏移量,但我将向您展示如何获取这些信息以及出错的指令。

假设我的代码是:

prev = [100] + range(3)
x = prev[prev[prev[0]]]

如果我运行它,我将得到一个IndexError 异常。但它是哪个“prev”?

trepan2 (或 trepan3k )在此处公开指令指针。它还可以访问反汇编器和解析器。那么让我们看看如何在这里使用它:

trepan2 /tmp/boom.py
-> 2 prev = [100] + range(3)
(trepan2) next
(/tmp/boom.py:3 @19): <module>
-- 3 x = prev[prev[prev[0]]]
(trepan2) next
(/tmp/boom.py:3 @32): <module>
!! 3 x = prev[prev[prev[0]]]
R=> (<type 'exceptions.IndexError'>, 'list index out of range', <traceback object at
(trepan2) info pc
PC offset is 32.
2 0 LOAD_CONST 0 100
3 BUILD_LIST 1
6 LOAD_NAME 0 0
9 LOAD_CONST 1 3
12 CALL_FUNCTION 1 1 positional, 0 keyword pair
15 BINARY_ADD None
16 STORE_NAME 1 1

3 19 LOAD_NAME 1 1
22 LOAD_NAME 1 1
25 LOAD_NAME 1 1
28 LOAD_CONST 2 0
31 BINARY_SUBSCR None
--> 32 BINARY_SUBSCR None
33 BINARY_SUBSCR None
34 STORE_NAME 2 2
37 LOAD_CONST 3 None
40 RETURN_VALUE None

好的。所以我们看到我们到底在哪里,偏移量 32(之前在偏移量@19 处停止后的@32),但这意味着什么? trepan 调试器会将其转换回 Python,因此您不必自己执行此操作:

(trepan2) deparse -p
instruction: 32 BINARY_SUBSCR
x = prev[prev[prev[0]]]
-------------
Contained in...
Grammar Symbol: binary_subscr
x = prev[prev[prev[0]]]
-------------------
(trepan2) prev
[100, 0, 1, 2]

然后,上面显示您处于偏移量 32(不是 31 或 33)并且特定的 prev 访问不是第一个访问 prev[0]prev[prev[0]] 之后的一个。

虽然在调试器中同时具有反汇编器和解析器,但您不必太多了解正在发生的事情。但我认为了解指令的作用或指令顺序是什么并没有什么坏处。

关于python - 使用反汇编程序调试错误的Python,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28435882/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com