Oracle® Developer Studio 12.5:性能分析器

退出打印视图

更新时间: 2016 年 6 月
 
 

带注释的反汇编代码

带注释的反汇编代码提供了函数或对象模块指令的汇编代码列表,以及与每个指令相关联的性能度量。 带注释的反汇编代码有多种显示方法,具体取决于行号映射和源文件是否可用,以及请求其带注释的反汇编代码的函数的对象模块是否已知。

  • 如果对象模块未知,则性能分析器只对指定函数的指令进行反汇编,并且不会在反汇编代码中显示任何源代码行。

  • 如果对象模块已知,则针对对象模块内的所有函数进行反汇编。

  • 如果源文件可用,并且记录了行号数据,则性能分析器可以根据显示首选项交错显示源代码和反汇编代码。

  • 如果编译器已向对象代码中插入注释,则它也可交错显示在反汇编代码中(如果已经设置了相应的首选项)。

反汇编代码中的每个指令都用以下信息进行注释:

  • 由编译器报告的源代码行号

  • 它的相对地址

  • 指令的十六进制表示(如果要求)

  • 指令的汇编程序 ASCII 表示

调用地址尽可能解析为符号(如函数名称)。度量显示在指令行上。如果设置了相应的首选项,度量还可显示在任何交错显示的源代码中。源代码注释的可能度量值如表 14所述。

对于在多个位置包含在 #include 中的代码,每次当代码包含在 #include 中时,代码的反汇编代码列表都会重复执行一次反汇编指令。只有在文件中首次显示反汇编代码的重复块时,源代码才会交叉。例如,如果在名为 inc_body.h 的头文件中定义的代码块分别由 inc_bodyinc_entryinc_middleinc_exit 四个函数 #included,则反汇编指令块将在 inc_body.h 的反汇编代码列表中出现四次,但是源代码仅在四个反汇编指令块的第一个块中交错显示。切换到 "Source"(源)视图,可以看到与每次重复的反汇编代码对应的索引行。

可以在 "Disassembly"(反汇编)视图中显示索引行。与 "Source"(源)视图不同的是,不能直接使用这些索引行进行导航。 将光标放在紧挨该索引行下方的其中一条指令上,然后选择 "Source"(源)视图,可以导航至该索引行中引用的文件。

对其他文件中的代码执行了 #include 操作的文件会将通过该操作包含的代码显示为不与源代码交错显示的原始反汇编指令。将光标放在这些指令中的其中一条指令上,然后选择 "Source"(源)视图,将打开包含 #included 代码的文件。在显示此文件时选择 "Disassembly"(反汇编)视图,将显示与源代码交错显示的反汇编代码。

内联函数的源代码可以与反汇编代码交错显示,但宏的源代码则不能与反汇编代码交错显示。

代码未优化时,每个指令的行号按顺序显示,源代码行和反汇编指令的交错显示以预期的方式进行。优化后,后面行的指令有时会显示在前面行的指令前面。分析器的交错显示算法为:只要指令显示为来自第 N 行,该行前所有源代码行(包括第 N 行)都会写入该指令前。优化的一个作用是源代码可以在控制转移指令与其延迟槽指令之间显示。与源代码的第 N 行关联的编译器注释就写在该行之前。

解释带注释的反汇编代码

解释带注释的反汇编代码并不是一项简单的工作。 叶 PC 是要执行的下一条指令的地址,因此归属到某个指令的度量应该视为等待执行该指令所用的时间。不过,指令的执行并不总是按顺序发生,而且在记录调用堆栈时可能发生延迟。要利用带注释的反汇编代码,应该熟悉记录实验的硬件以及装入和执行指令的方式。

接下来的几个小节将讨论有关解释带注释的反汇编代码的一些问题。

指令发送分组

指令按组装入和发送(称为指令发送组)。 组中包括哪些指令取决于硬件、指令类型、已执行的指令以及与其他指令或寄存器的各种相关性。因此,某些指令可能会被误解,因为它们始终与前一条指令在同一个时钟周期内发送,所以它们从不单独表示下一条要执行的指令。在记录调用堆栈时,可能有多条指令被视为是下一条要执行的指令。

指令发送规则因处理器类型的不同而不同,并且还取决于高速缓存行内的指令对齐。由于链接程序比高速缓存行要求执行更精确的指令对齐,因此在看上去不相关的函数中进行的更改可能会导致指令的对齐方式不同。不同的对齐方式会引起性能的改进或降级。

下面列举了一种假设情况,即同一个函数在略微不同的环境下进行编译和链接的情况。以下两个输出示例均为 er_print 实用程序的带注释的反汇编代码列表。两个示例中的指令是相同的,但指令的对齐方式不同。

在以下输出示例中,指令的对齐方式是将 cmpbl,a 两条指令映射到不同的高速缓存行。大量的时间都用于等待执行这两条指令。

   Excl.     Incl.
User CPU  User CPU
    sec.      sec.
                             1. static int
                             2. ifunc()
                             3. {
                             4.     int i;
                             5.
                             6.     for (i=0; i<10000; i++)
                                <function: ifunc>
   0.010     0.010              [ 6]    1066c:  clr         %o0
   0.        0.                 [ 6]    10670:  sethi       %hi(0x2400), %o5
   0.        0.                 [ 6]    10674:  inc         784, %o5
                             7.         i++;
   0.        0.                 [ 7]    10678:  inc         2, %o0
## 1.360     1.360              [ 7]    1067c:  cmp         %o0, %o5
## 1.510     1.510              [ 7]    10680:  bl,a        0x1067c
   0.        0.                 [ 7]    10684:  inc         2, %o0
   0.        0.                 [ 7]    10688:  retl
   0.        0.                 [ 7]    1068c:  nop
                             8.     return i;
                             9. }

在下一个输出示例中,指令的对齐方式是将 cmpbl,a 两条指令映射到相同的高速缓存行。大量的时间都用于等待执行其中一条指令。

   Excl.     Incl.
User CPU  User CPU
    sec.      sec.
                             1. static int
                             2. ifunc()
                             3. {
                             4.     int i;
                             5.
                             6.     for (i=0; i<10000; i++)
                                <function: ifunc>
   0.        0.                 [ 6]    10684:  clr         %o0
   0.        0.                 [ 6]    10688:  sethi       %hi(0x2400), %o5
   0.        0.                 [ 6]    1068c:  inc         784, %o5
                             7.         i++;
   0.        0.                 [ 7]    10690:  inc         2, %o0
## 1.440     1.440              [ 7]    10694:  cmp         %o0, %o5
   0.        0.                 [ 7]    10698:  bl,a        0x10694
   0.        0.                 [ 7]    1069c:  inc         2, %o0
   0.        0.                 [ 7]    106a0:  retl
   0.        0.                 [ 7]    106a4:  nop
                             8.     return i;
                             9. }

指令发送延迟

有时,会更频繁地出现特定的叶 PC,因为这些 PC 表示的指令在发送前被延迟。导致出现这种情况的原因有多种,下面列出了其中的一些原因:

  • 执行前一条指令用了很长的时间,并且执行不能中断,例如指令陷入内核中时。

  • 运算指令需要的寄存器不可用,因为该寄存器的内容是由前面尚未完成的指令设置的。具有数据高速缓存未命中的装入指令就是这种延迟的一个示例。

  • 浮点运算指令正在等待另一个浮点指令完成。指令不能流水线作业时会出现这种情况,例如平方根和浮点除法。

  • 指令高速缓存不存在包含指令的内存字(指令高速缓存未命中)。

硬件计数器溢出的归属

除某些平台上的 TLB 未命中和 precise 计数器外,硬件计数器溢出事件的调用堆栈按指令的顺序记录在后续的某些点上,而不是记录在发生溢出的点上 这种延迟出于多种原因,其中一个是处理由溢出产生的中断需要一定时间。对于某些计数器(如发送的周期或指令),这种延迟无关紧要。对于其他计数器(例如,那些计数高速缓存未命中或浮点运算的计数器),度量被归属到导致溢出的指令之外的其他指令。

通常,引起事件的 PC 只是记录的 PC 前的几条指令,而这些指令可以在反汇编代码列表中正确定位。 但是,如果该指令范围内存在分支目标,那么要确定哪条指令对应于引起事件的 PC 是很困难的,甚至是不可能的。

如果系统中的处理器具有使用 precise 关键字标记的计数器,则允许进行内存空间分析,而不需要任何特殊的二进制文件编译。例如,SPARC T4 和 M7 处理器提供了多个 precise 计数器。运行 collect -h 命令并查找 precise 关键字,可确定系统是否允许内存空间分析。

例如,在配备了 SPARC T7 处理器的系统上运行以下命令时,将会显示可用的 precise 原始计数器:

$ collect -h | grep -i precise | grep -v alias
           Counters labeled as precise in the list below will collect memory-space data by default.
    loads      Instr_ld       precise load-store     events 0123 Load Instructions
    stores     Instr_st       precise load-store     events 0123 Store Instructions
    dcm        DC_miss_commit precise load-store     events 0123 L1 D-cache Misses
        use the 'precise' counter:
    Instr_ld                  precise load-store     events 0123
    Instr_st                  precise load-store     events 0123
    Instr_SPR_ring_ops        precise load-store     events 0123
    Instr_atomic              precise load-store     events 0123
    Instr_SW_prefetch         precise load-store     events 0123
    Instr_block_ld_st         precise load-store     events 0123
    DC_miss_L2_L3_hit_commit  precise load-store     events 0123
                              precise load-store     events 0123
                              precise load-store     events 0123
    DC_miss_commit            precise load-store     events 0123