通过 CPython 执行跟踪 Python 字节码
Posted
技术标签:
【中文标题】通过 CPython 执行跟踪 Python 字节码【英文标题】:Trace Python bytecodes through CPython execution 【发布时间】:2015-11-11 02:46:14 【问题描述】:我试图通过跟踪不同的字节码/操作码来加深我对 CPython 解释器的理解,因为它们通过 ceval.c
中的解释器循环,用于一个简单的 Python 程序。我使用bytecode
和opcode
表示相同的意思。
我的python程序是这样的:
#filename: test.py
x = 2
y = 3
if x < y:
z = x
elif True:
z = y
else:
z = 100
我正在使用 Python 2.7.8 并使用如下调试标志构建它:
wget https://www.python.org/ftp/python/2.7.8/Python-2.7.8.tgz # download
tar xvf Python-2.7.8.tgz # extract
cd Python2.7.8
./configure --with-pydebug # build with debug flag
make -j # parallel make
我有兴趣在解释器循环 ceval.c
中跟踪 switch
语句周围的 for(;;)
循环,从第 964 行开始。
我在 for
循环开始后添加了这些行,以检查解释器是否正在运行我的文件,如果是,则打印出 opcode
。
964 for (;;)
965 if (strcmp(filename, "../test.py") == 0)
966 printf("%d\n", opcode);
967
我得到的输出是(手动添加 cmets 以显示来自 opcode.h
的 opcode
DEFINE
):
$ ./python.exe ../test.py | cat -n
1 0 // STOP_CODE
2 90 // HAVE_ARGUMENT
3 90 // HAVE_ARGUMENT
4 101 // LOAD_NAME
5 101 // LOAD_NAME
6 101 // LOAD_NAME
7 90 // STORE_NAME
我期望 12 个不同的操作码而不是 7 个,因为当我得到同一个文件的字节码反汇编时,有 12 个字节码命令。
$ ./python.exe -m dis ../pytests/test.py | sed "/^$/d" | cat -n
1 1 0 LOAD_CONST 0 (2)
2 3 STORE_NAME 0 (x)
3 2 6 LOAD_CONST 1 (3)
4 9 STORE_NAME 1 (y)
5 4 12 LOAD_NAME 0 (x)
6 15 LOAD_NAME 1 (y)
7 18 COMPARE_OP 0 (<)
8 21 POP_JUMP_IF_FALSE 33
9 5 24 LOAD_NAME 0 (x)
10 27 STORE_NAME 2 (z)
11 30 JUMP_FORWARD 21 (to 54)
12 6 >> 33 LOAD_NAME
我对 CPython 解释器如何工作的心智模型和/或我记录不同操作码的方法不正确,或两者兼而有之。你能解释一下为什么我从ceval.c
文件的输出和使用python -m dis
包看到不同的操作码吗?
【问题讨论】:
当您的意思是“CPython”(“标准" Python 解释器)。就目前而言,这个问题没有太大意义...... 但是忽略您看到的字节码不同的事实,我认为您希望得到不同数量的字节码:ceval 中的循环只会显示执行的内容(而不是“it/else”的部分跳过)而 dis 会给你一切。 What is the best way to sample/profile a PyObjC application?的可能重复 【参考方案1】:您的跟踪显示有问题,因为在正常情况下,您不会执行 STOP_CODE
(值 0)会停止执行。此外,HAVE_ARGUMENT
不是操作码。对于 Python 2.7,操作码是 STORE_NAME
。
至于值的差异,这在任何不是直线(基本块)代码的代码中都是可以预料的。你的不是直线代码。有一个COMPARE <
后跟一个POP_JUMP_IF_FALSE
跳转。
【讨论】:
以上是关于通过 CPython 执行跟踪 Python 字节码的主要内容,如果未能解决你的问题,请参考以下文章