如何使用gcc确定嵌入式系统中的最大堆栈使用率?

如何使用gcc确定嵌入式系统中的最大堆栈使用率?,gcc,embedded,code-analysis,static-analysis,Gcc,Embedded,Code Analysis,Static Analysis,我正在为嵌入式系统编写启动代码——在跳转到main()函数之前加载初始堆栈指针的代码——我需要告诉它我的应用程序将使用多少字节的堆栈(或者一些更大的保守估计) 我被告知gcc编译器现在有一个-fstack usage选项和-fcallgraph info选项,可以以某种方式用于静态地为我计算准确的“最大堆栈使用率”。 (作者:博特卡祖、科马尔和海因克) Nigel Jones说递归在嵌入式系统中是一个非常糟糕的想法(“计算堆栈大小”2009),所以我一直很小心,没有在这段代码中使用任何相互递归的

我正在为嵌入式系统编写启动代码——在跳转到main()函数之前加载初始堆栈指针的代码——我需要告诉它我的应用程序将使用多少字节的堆栈(或者一些更大的保守估计)

我被告知gcc编译器现在有一个-fstack usage选项和-fcallgraph info选项,可以以某种方式用于静态地为我计算准确的“最大堆栈使用率”。 (作者:博特卡祖、科马尔和海因克)

Nigel Jones说递归在嵌入式系统中是一个非常糟糕的想法(“计算堆栈大小”2009),所以我一直很小心,没有在这段代码中使用任何相互递归的函数

此外,我确保在中断指令最终返回之前,我的中断处理程序都不会重新启用中断,所以我不需要担心重新进入的中断处理程序

如果没有递归或重入中断处理程序,应该可以静态地确定最大堆栈使用量。(因此,大多数答案并不适用)。 我的理解是,当每个中断处理程序没有被更高优先级的中断中断时,我(或者最好是在我的电脑上,每次重建可执行文件时自动运行的一些代码)首先找到最大堆栈深度,当main()函数没有中断时,找到最大堆栈深度。 然后我把它们加起来,找到总的(最坏情况下的)最大堆栈深度。(在我的嵌入式系统中)当main()后台任务被最低优先级的中断中断时处于其最大深度,当被下一个最低优先级的中断中断时,该中断处于其最大深度,依此类推

我正在使用带有GCC4.6.0的YAGARTO为LM3S1968 ARM-M3编译代码

那么,如何在gcc中使用-fstack usage选项和-fcallgraph info选项来计算最大堆栈深度呢?还是有更好的方法来确定最大堆栈使用率


(有关针对Keil编译器的几乎相同的问题,请参阅。)

我不熟悉
-fstack用法
-fcallgraph info
选项。但是,始终可以通过以下方式计算实际堆栈使用情况:

  • 分配足够的堆栈空间(用于此实验),并将其初始化为易于识别的内容。我喜欢
    0xee
  • 运行应用程序并测试其所有内部路径(通过输入和参数的所有组合)。让它运行超过“足够长的时间”
  • 检查堆栈区域,查看使用了多少堆栈
  • 使堆栈大小增加10%或20%,以容忍软件更新和罕见情况
  • GCC文件:

    -fstack用法

    根据每个函数生成程序的编译器输出堆栈使用信息。转储文件名是通过在auxname后面附加.su来生成的。auxname是从输出文件的名称生成的,如果显式指定并且它不是可执行文件,则它是源文件的基本名称。条目由三个字段组成:

    • 函数的名称
    • 字节数
    • 一个或多个限定符:静态、动态、有界
    限定符static表示函数静态地操作堆栈:在函数进入时为帧分配固定数量的字节,在函数退出时释放;在函数中不进行堆栈调整。第二个字段是这个固定的字节数

    限定符dynamic意味着函数可以动态地操作堆栈:除了上面描述的静态分配之外,还可以在函数体中进行堆栈调整,例如围绕函数调用的push/pop参数。如果还存在限定符bounded,则这些调整量在编译时是有界的,第二个字段是函数使用的堆栈总量的上限。如果不存在,则这些调整量在编译时不受限制,第二个字段仅表示有限制的部分

    我找不到对-fcallgraph info的任何引用

    您可以通过-fstack usage和-fdump tree-optimized创建所需的信息


    对于-fdump树中优化的每个叶,从-fstack usage中获取其父级并求和它们的堆栈大小数(请记住,该数字适用于任何具有“动态”但非“有界”的函数),找到这些值的最大值,这应该是您的最大堆栈使用量。

    以防没有人给出更好的答案,我会将我在评论中的内容发布到你的另一个问题上,尽管我没有使用这些选项和工具的经验:

    GCC 4.6增加了
    -fstack usage
    选项,该选项提供了基于函数的堆栈使用统计信息

    如果您将这些信息与由或类似工具生成的调用图相结合,就可以得到您想要的堆栈深度分析(编写脚本可能非常容易)。让脚本读取堆栈使用信息,并加载函数名与该函数使用的堆栈的映射。然后让脚本遍历
    cflow
    图(这可能是一个易于解析的文本树),为调用图中的每个分支添加与每行相关联的堆栈使用情况


    因此,看起来这可以通过GCC实现,但您可能必须拼凑出正确的工具集。

    已经很晚了,但对于任何关注这一点的人来说,所给出的答案涉及到将fstack用法和调用图工具(如cflow)的输出结合起来,对于任何动态分配,甚至是有界的,因为没有关于动态堆栈分配何时发生的信息。因此,不可能知道应该对哪些函数应用该值。作为人为的例子,如果(简化)
    main        1024     dynamic,bounded
    functionA    512     static
    functionB     16     static
    
    main
        functionA
        functionB
    
    static unsigned long long max_stack_usage = 0;
    
    void __cyg_profile_func_enter(void * this, void * call) __attribute__((no_instrument_function)) {
          // get current stack usage using some hardware facility or intrisic function
          // like __get_SP() on ARM with CMSIS
          unsigned cur_stack_usage = __GET_CURRENT_STACK_POINTER() - __GET_BEGINNING_OF_STACK();
          // use debugger to output current stack pointer
          // for example semihosting on ARM
          __DEBUGGER_TRANSFER_STACK_POINTER(cur_stack_usage);
          // or you could store the max somewhere
          // then just run the program
          if (max_stack_usage < cur_stack_usage) {
                max_stack_usage = max_stack_usage;
          }
          // you could also manually inspect with debugger
          unsigned long long somelimit = 0x2000;
          if (cur_stack_usage > somelimit) {
               __BREAKPOINT();
          }
    }
    void __cyg_profile_func_exit(void * this, void * call) __attribute__((no_instrument_function)) {
          // well, nothing
    }