通过 CPython 执行跟踪 Python 字节码

Posted

技术标签:

【中文标题】通过 CPython 执行跟踪 Python 字节码【英文标题】:Trace Python bytecodes through CPython execution 【发布时间】:2015-11-11 02:46:14 【问题描述】:

我试图通过跟踪不同的字节码/操作码来加深我对 CPython 解释器的理解,因为它们通过 ceval.c 中的解释器循环,用于一个简单的 Python 程序。我使用bytecodeopcode 表示相同的意思。

我的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.hopcode 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 &lt; 后跟一个POP_JUMP_IF_FALSE 跳转。

【讨论】:

以上是关于通过 CPython 执行跟踪 Python 字节码的主要内容,如果未能解决你的问题,请参考以下文章

从 C 扩展跟踪 CPython 对象的生命周期

Python解释器

深入理解 python 虚拟机:字节码教程——原来装饰器是这样实现的

Python 的种类以及特点

python-执行过程

python_1 python的编译过程