用于获取正确调用堆栈的 ARM 标头 [英] ARM Headers to Get Proper Call Stacks

查看:15
本文介绍了用于获取正确调用堆栈的 ARM 标头的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在 ARM 处理器上对基于 linux 的软件本身进行优化.这些优化主要以 ARM 和 ARM NEON 函数的形式进行.

I am currently carrying out optimizations on a linux-based software itself on an ARM processor. Those optimizations are mostly in the form of ARM and ARM NEON functions.

为了对软件进行概要分析,我使用了性能记录和火焰图,但是,一旦我介绍了汇编函数,它们就不会堆叠在调用它们的函数之上,而是看似随机的位置.

In order to profile the software I use perf record and flame-graphs, however, once I introduce the assembler functions, they do not stack on top of the functions that call them but rather seemingly random places.

因此,我的问题是,我应该在我的函数中包含哪些内容,以便它们在调用堆栈中正确显示.

My question therefore was, what should I include in my functions for them to appear properly in the call stacks.

有一个稍微相关的话题,但没有给出好的答案如何使用 gcc 编译代码和 ARM Cortex A8 目标进行调用图分析?.我使用相同的标志加上 mapcs-frame.

There was a slightly related topic but no good answer was given How to get call graph profiling working with gcc compiled code and ARM Cortex A8 target?. I use the same flags plus mapcs-frame.

下面,我举一个C函数被GCC翻译成ARM的例子.这个 ARM 函数似乎产生了不错的堆栈,但我想了解为什么.

Below, I give an example of a C function translated to ARM by GCC. This ARM function seems to produces decent stacks but I would like to understand why.

int half(int in);
int sum(int in1, int in2);
int mean(int in1, int in2);

int half(int i)
{
    return i / 2;
}

int sum(int i, int j)
{
    return i + j;
}

int mean(int i, int j)
{
    int s = sum(i, j);
    int m = half(s);
    return m;
}

int main()
{
    int a = 1;
    int b = 5;
    int i;
    int result;
    for (i = 0; i<10000000; i++) { 
        result = mean(a, b);
    }
    return 0;
}

.cpu cortex-a9
        .eabi_attribute 27, 3
        .eabi_attribute 28, 1
        .fpu neon
        .eabi_attribute 20, 1
        .eabi_attribute 21, 1
        .eabi_attribute 23, 3
        .eabi_attribute 24, 1
        .eabi_attribute 25, 1
        .eabi_attribute 26, 2
        .eabi_attribute 30, 6
        .eabi_attribute 34, 1
        .eabi_attribute 18, 4
        .file   "a.c"
        .text
        .align  2
        .global half
        .type   half, %function
    half:
        @ args = 0, pretend = 0, frame = 8
        @ frame_needed = 1, uses_anonymous_args = 0
        mov ip, sp
        stmfd   sp!, {fp, ip, lr, pc}
        sub fp, ip, #4
        sub sp, sp, #8
        str r0, [fp, #-16]
        ldr r3, [fp, #-16]
        mov r2, r3, lsr #31
        add r3, r2, r3
        mov r3, r3, asr #1
        mov r0, r3
        sub sp, fp, #12
        ldmfd   sp, {fp, sp, pc}
        .size   half, .-half
        .align  2
        .global sum
        .type   sum, %function
    sum:
        @ args = 0, pretend = 0, frame = 8
        @ frame_needed = 1, uses_anonymous_args = 0
        mov ip, sp
        stmfd   sp!, {fp, ip, lr, pc}
        sub fp, ip, #4
        sub sp, sp, #8
        str r0, [fp, #-16]
        str r1, [fp, #-20]
        ldr r2, [fp, #-16]
        ldr r3, [fp, #-20]
        add r3, r2, r3
        mov r0, r3
        sub sp, fp, #12
        ldmfd   sp, {fp, sp, pc}
        .size   sum, .-sum
        .align  2
        .global mean
        .type   mean, %function
    mean:
        @ args = 0, pretend = 0, frame = 16
        @ frame_needed = 1, uses_anonymous_args = 0
        mov ip, sp
        stmfd   sp!, {fp, ip, lr, pc}
        sub fp, ip, #4
        sub sp, sp, #16
        str r0, [fp, #-24]
        str r1, [fp, #-28]
        ldr r1, [fp, #-28]
        ldr r0, [fp, #-24]
        bl  sum
        str r0, [fp, #-16]
        ldr r0, [fp, #-16]
        bl  half
        str r0, [fp, #-20]
        ldr r3, [fp, #-20]
        mov r0, r3
        sub sp, fp, #12
        ldmfd   sp, {fp, sp, pc}
        .size   mean, .-mean
        .align  2
        .global main
        .type   main, %function
    main:
        @ args = 0, pretend = 0, frame = 16
        @ frame_needed = 1, uses_anonymous_args = 0
        mov ip, sp
        stmfd   sp!, {fp, ip, lr, pc}
        sub fp, ip, #4
        sub sp, sp, #16
        mov r3, #1
        str r3, [fp, #-20]
        mov r3, #5
        str r3, [fp, #-24]
        mov r3, #0
        str r3, [fp, #-16]
        b   .L8
    .L9:
        ldr r1, [fp, #-24]
        ldr r0, [fp, #-20]
        bl  mean
        str r0, [fp, #-28]
        ldr r3, [fp, #-16]
        add r3, r3, #1
        str r3, [fp, #-16]
    .L8:
        ldr r2, [fp, #-16]
        movw    r3, #38527
        movt    r3, 152
        cmp r2, r3
        ble .L9
        mov r3, #0
        mov r0, r3
        sub sp, fp, #12
        ldmfd   sp, {fp, sp, pc}
        .size   main, .-main
        .ident  "GCC: (crosstool-NG linaro-1.13.1-4.9-2014.09 - Linaro GCC 4.9-2014.09) 4.9.2 20140904 (prerelease)"
        .section    .note.GNU-stack,"",%progbits

-------------------编辑-------------------

-------------------EDIT-------------------

这是我尝试集成的功能类型的示例.在链接方面,它所做的只是保存开头的堆栈和链接寄存器并将它们设置为结尾.我应该添加什么?

Here is the example of the kind of function I am trying to integrate. In terms of linkage, all it does is save the stack and link register at the beginning and set them a the end. What should I add to it?

.section .text

.global ARM_smoothing

ARM_smoothing:
    STMFD       sp!, {r4-r12,lr} //move used registers on stack (avoid segmentation fault)
    MOV         r5, r0              
    ADD         r0, r0, r2
    ADD         r0, r0, r2
    MOV         r8, r0
    ADD         r8, r8, r2
    ADD         r8, r8, r2       //the 6 instructions create 3 pointers to the row above and below as well as the current one
    ADD         r1, r1, r2
    ADD         r1, r1, r2       
    ADD         r1, r1, #2       //move destination pointer to first element (1 row down, 1 element left)
    SUB         r2, r2, #2
    SUB         r3, r3, #2       //counters decremented because smoothing function works with a margin of 1 on every side
    LDR         r9, =0x1C71C71D  //(1/9)*2^32 pour effectuer la division par 9
    LDR         r10, =0x2
    LDR         r11, =0xC        //shifts for pointers to data  
    VLDR.U64    d20, =0x1C71C71D //(1/9)*2^32 pour effectuer la division par 9
    VLDR.U64    d22, =0x0        //initialization of zeros to be used (not ncessarily needed)
    VLDR.U64    d23, =0x0
    VDUP.32     d20, d20[0]      //initialize vector for multiplication
height_loop:
    MOV         r4, r2           //reset width counter
    CMP         r4, #8
    BLGE        width_loop_eight_smoothing //use neon while more than 8 elements in row need smoothing
    CMP         r4, #1
    BLGE        width_loop_rest  //use normal ARM for remaining elements, can't do in NEON because of margin
    ADD         r0, r0, #4       //skip margin
    ADD         r1, r1, #4
    ADD         r5, r5, #4
    ADD         r8, r8, #4
    SUBS        r3, r3, #1       //decrement row counter
    BNE         height_loop      //loop while there still are rows
    LDMFD       sp!, {r4-r12,pc} //restore stack and return to calling function


width_loop_eight_smoothing:
    SUB         r4, r4, #8       //decrement width counter
    VLD1.16     {d0, d1}, [r5], r10     //load upper left elements
    VLD1.16     {d2, d3}, [r5], r10     //load upper middle elements
    VADDL.S16   q2, d0, d2              //long addition of elements to be sure to not lose any data
    VADDL.S16   q3, d1, d3              
    VLD1.16     {d0, d1}, [r5], r11     //load upper right elements     
    VLD1.16     {d2, d3}, [r0], r10     //load middle left elements
    VADDL.S16   q4, d0, d2
    VADDL.S16   q5, d1, d3
    VADD.S32    q2, q4                  //add to grand total
    VADD.S32    q3, q5
    VLD1.16     {d0, d1}, [r0], r10     //load current elements
    VLD1.16     {d2, d3}, [r0], r11     //load middle right elements
    VADDL.S16   q4, d0, d2
    VADDL.S16   q5, d1, d3
    VADD.S32    q2, q4
    VADD.S32    q3, q5
    VLD1.16     {d0, d1}, [r8], r10     //load lower left elements
    VLD1.16     {d2, d3}, [r8], r10     //load lower middle elements
    VADDL.S16   q4, d0, d2
    VADDL.S16   q5, d1, d3
    VADD.S32    q2, q4
    VADD.S32    q3, q5
    VLD1.16     {d0, d1}, [r8], r11     //load lower right elements
    VADDL.S16   q4, d0, d22
    VADDL.S16   q5, d1, d23
    VADD.S32    q2, q4
    VADD.S32    q3, q5  
    VMULL.S32   q6, d4, d20             //divide by 9 (upper element is total divided by 9)
    VMULL.S32   q7, d5, d20
    VMULL.S32   q8, d6, d20
    VMULL.S32   q9, d7, d20
    VUZP.32     q6, q7                  //pack results into less registers and smaller elements
    VUZP.32     q8, q9
    VUZP.16     q7, q9
    VSHR.U16    q8, q7, #15             //when multiplied element is negative, result is always one under
    VADD.S16    q7, q8                  //rectifying by adding sign bit to total
    VST1.16     {d14, d15}, [r1]!       //store results
    CMP         r4, #8                  //check if theres enough elements to do 8 more in NEON          
    BCS         width_loop_eight_smoothing      //if yes, loop neon code
    MOV         PC, LR                  //return to ARM_smoothing if not



width_loop_rest:                        //works similaarly to NEON but one element at a time
    LDRSH       r6, [r0], #2            //converts loaded half words to signed full words
    LDRSH       r7, [r0]                //main difference is with the way increments are done since there is an overlap
    ADD         r6, r7, r6
    LDRSH       r7, [r0, #2]
    ADD         r6, r7, r6
    LDRSH       r7, [r5], #2
    ADD         r6, r7, r6
    LDRSH       r7, [r5]
    ADD         r6, r7, r6
    LDRSH       r7, [r5, #2]
    ADD         r6, r7, r6
    LDRSH       r7, [r8], #2
    ADD         r6, r7, r6
    LDRSH       r7, [r8]
    ADD         r6, r7, r6
    LDRSH       r7, [r8, #2]
    ADD         r6, r7, r6  
    SMULLS      r6, r7, r6, r9
    ADDMI       r7, #1
    STRH        r7, [r1], #2
    SUBS        r4, #1          //decrement width counter and check if there's any left
    BNE         width_loop_rest
    MOV         PC, LR

推荐答案

您可以清楚地看到编译器如何使用一些伪操作来注释汇编器...

You can clearly see how the compiler is annotating the assembler with some pseudo-ops...

    .global mean
    .type   mean, %function

...

   .size   mean, .-mean   

这些放在 COFF 部分中,需要进行构建,以便调用图工具可以知道 PC 范围对于您的汇编函数是什么.

These are put in COFF sections and need to make it to a build so that the call graph tools can know what PC range is for your assembler function.

    .global ARM_smoothing
 +  .type ARM_smoothing, %function

...

 +  .size ARM_smoothing, .-ARM_smoothing

其他伪操作取决于所需的调试信息.

Other pseudo-ops depend on the debug information needed.

其他有.fnend.fnstart.movsp.save.setfp

这取决于工具预期的调试/对象格式.还有两种类型的数据;

It depends on the debug/object format expected by the tool. There are also two types of data;

  1. 代码范围信息
  2. 堆栈和框架的使用

展开(或堆栈回溯)通常需要两者,但采样性能工具可能只能使用第一个.进行对象清理的异常处理代码需要最多的信息.

Both are typically needed for unwinding (or a stack back trace) but a sampling performance tool might only get away with the first. Exception handling code that does object clean up requires the most information.

相关:ARM 链接和帧寄存器

这篇关于用于获取正确调用堆栈的 ARM 标头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆