我应该如何理解dis.dis的输出?


76

我想了解如何使用dis(Python字节码的反汇编程序)。具体来说,应该如何解释dis.dis(或dis.disassemble)的输出?

这是一个非常具体的示例(在Python 2.7.3中):

dis.dis("heapq.nsmallest(d,3)")

      0 BUILD_SET             24933
      3 JUMP_IF_TRUE_OR_POP   11889
      6 JUMP_FORWARD          28019 (to 28028)
      9 STORE_GLOBAL          27756 (27756)
     12 LOAD_NAME             29811 (29811)
     15 STORE_SLICE+0  
     16 LOAD_CONST            13100 (13100)
     19 STORE_SLICE+1

我看到JUMP_IF_TRUE_OR_POP等是字节码指令(尽管有趣的是,BUILD_SET它没有出现在此列表中,尽管我希望它可以作为BUILD_TUPLE。我认为右侧的数字是内存分配,而左侧的数字是goto数字...我注意到它们每次几乎增加3(但不是完全一样)。

如果我包装dis.dis("heapq.nsmallest(d,3)")一个函数:

def f_heapq_nsmallest(d,n):
    return heapq.nsmallest(d,n)

dis.dis("f_heapq(d,3)")

      0 BUILD_TUPLE            26719
      3 LOAD_NAME              28769 (28769)
      6 JUMP_ABSOLUTE          25640
      9 <44>                                      # what is <44> ?  
     10 DELETE_SLICE+1 
     11 STORE_SLICE+1 

Answers:


96

您正在尝试拆解内含源代码的字符串,但是这不支持dis.dis在Python 2.用字符串参数,它把字符串,如果它包含字节码(见函数disassemble_stringdis.py)。因此,您会看到基于将源代码误解为字节码而产生的无意义的输出。

Python 3中的情况有所不同,Python 3在反汇编之前先dis.dis编译字符串参数

Python 3.2.3 (default, Aug 13 2012, 22:28:10) 
>>> import dis
>>> dis.dis('heapq.nlargest(d,3)')
  1           0 LOAD_NAME                0 (heapq) 
              3 LOAD_ATTR                1 (nlargest) 
              6 LOAD_NAME                2 (d) 
              9 LOAD_CONST               0 (3) 
             12 CALL_FUNCTION            2 
             15 RETURN_VALUE         

在Python 2中,您需要先编译代码,然后再将其传递给dis.dis

Python 2.7.3 (default, Aug 13 2012, 18:25:43) 
>>> import dis
>>> dis.dis(compile('heapq.nlargest(d,3)', '<none>', 'eval'))
  1           0 LOAD_NAME                0 (heapq)
              3 LOAD_ATTR                1 (nlargest)
              6 LOAD_NAME                2 (d)
              9 LOAD_CONST               0 (3)
             12 CALL_FUNCTION            2
             15 RETURN_VALUE        

这些数字是什么意思?数1在最左边是在从其中该字节代码被编译的源代码的行号。左列中的数字是指令在字节码中的偏移量,而右列中的数字是opargs。让我们看一下实际的字节码:

>>> co = compile('heapq.nlargest(d,3)', '<none>', 'eval')
>>> co.co_code.encode('hex')
'6500006a010065020064000083020053'

我们在字节码的偏移量0处找到了oparg65的操作码; 那么(在偏移量3处)是操作码,操作参数,以此类推。请注意,opargs的顺序为little-endian,即数字1。未记录的模块包含一些表,这些表为您提供每个操作码的名称,并为您提供每个名称的操作码:LOAD_NAME00006aLOAD_ATTR01000100opcodeopnameopmap

>>> opcode.opname[0x65]
'LOAD_NAME'

oparg的含义取决于操作码,对于全文,您需要阅读中的CPython虚拟机的实现ceval.c。ForLOAD_NAMELOAD_ATTRoparg是co_names代码对象属性的索引:

>>> co.co_names
('heapq', 'nlargest', 'd')

因为LOAD_CONST它是co_consts代码对象属性的索引:

>>> co.co_consts
(3,)

对于CALL_FUNCTION,它是传递给函数的参数数量,以16位编码,低字节为普通参数的数量,高字节为关键字参数的数量。


3
令人惊讶的是,是否有任何参考资料/教程/书籍包含所有这些低级详细信息。我想进一步挖掘
DevC

3
@DevC:代码对象随inspect模块一起记录。字节代码指令随dis模块一起记录。有关CPython虚拟机的实现细节,您必须阅读中的源代码ceval.c
加雷斯·里斯

因此,本次显示的内容为little-endian,长度为2个字节oparg
schemacs 2014年


有关ceval.c您的文档的@GarethRees可以参考执行模型docs.python.org/3.3/reference/executionmodel.html
KeatsKelleher

61

我将我对另一个问题的答案重新发布,以确保在谷歌搜索时能找到它dis.dis()


为了完善Gareth Rees的答案,这里仅是一小列的摘要,以解释反汇编字节码的输出。

例如,给定此功能:

def f(num):
    if num == 42:
        return True
    return False

可以将其分解为(Python 3.6):

(1)|(2)|(3)|(4)|          (5)         |(6)|  (7)
---|---|---|---|----------------------|---|-------
  2|   |   |  0|LOAD_FAST             |  0|(num)
   |-->|   |  2|LOAD_CONST            |  1|(42)
   |   |   |  4|COMPARE_OP            |  2|(==)
   |   |   |  6|POP_JUMP_IF_FALSE     | 12|
   |   |   |   |                      |   |
  3|   |   |  8|LOAD_CONST            |  2|(True)
   |   |   | 10|RETURN_VALUE          |   |
   |   |   |   |                      |   |
  4|   |>> | 12|LOAD_CONST            |  3|(False)
   |   |   | 14|RETURN_VALUE          |   |

每列都有特定目的:

  1. 源代码中对应的行号
  2. (可选)指示当前执行的指令(例如,当字节码来自帧对象时
  3. 一个标签,表示JUMP从较早的说明到本说明的可能
  4. 字节码中与字节索引相对应的地址(这些地址为2的倍数,因为Python 3.6每条指令使用2个字节,但在以前的版本中可能有所不同)
  5. 指令名称(也称为opname),每一个都是在简要介绍dis模块及其实施中可以找到ceval.c(CPython的核心环)
  6. Python内部使用的指令自变量(如果有的话)来获取一些常量或变量,管理堆栈,跳转到特定指令等。
  7. 指令论的人性化解释

4
这就是我正在寻找的!但是我在官方文档中找不到,thx
Tarjintor
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.