0

注意:我意识到我的问题并不清楚。我现在已经修改了它,并为首先犯的错误道歉。

我有一个要在嵌入式系统上运行的大型C 项目。我使用ARM 编译器。代码分布在多个子文件夹中,由 .c 和 .h 文件组成。

我想盘点一下什么函数被调用了多少次,这样我就可以识别死代码,也可以加速最常用的函数。ARM 编译器有一些用于删除无法访问的代码的选项,当函数指针起作用时它会失败所以,我想遍历代码的每个分支并记录对函数的调用次数。

例如(这是一个非常简单的程序来演示我在寻找什么,而不是原始代码):

void foo1(void)
{
    printf("Number is divisible by 5");
}
void foo2(void)
{
    printf("Number is divisible by 10");
}
void foo3(void)
{
    printf("Number is divisible by neither 10 nor 5");
}
void foo4(void)
{
    printf("foo4 called");
}


int main (void)
{
    int x;

    x = rand (11);

    if (! x%10)
    {
        foo2();
        foo1();
    }
    else if (! x%5)       foo1();
    else             foo3();

    return 0;
}

我想运行整个代码以便访问 if 分支的所有部分(即 foo1、foo2 和 foo3)。这将帮助我确定哪个函数被调用了多少次。在上面的示例中,foo1() 的调用频率高于 foo2(),而 foo4() 从未被调用。因此,识别和删除 foo4() 并优化 foo1() 是有意义的。

运行整个代码的任何想法/工具?

我认为的一种方法是修改 main 函数,如下所示:

int main (void)
{
    int x ;

    x = rand (11);

    //*************************************************
    //starting modification
    int a = 1; //added by me
    if_1: //added by me
    if (a == 1)
    {
        foo1(); //original code
        foo2(); //original code

        a=2; //added by me
        goto if_1; //added by me
    }

    else if (a==2)
    {
        foo2(); //original code
        a=3; //added by me
        goto if_1: //added by me
    }
    else             foo3(); //original code
    //end of modification
    //********************************************************        

    return 0;
}

这样它就可以通过原始代码运行。知道如何进行这种类型的修改吗?

4

3 回答 3

2
int main (void)
{
    int x = 5;
#ifdef DEBUG
    int debug_i, debug_data[] = { 5, 10, 20 };
    for(debug_i = 0;debug_i<sizeof debug_data / sizeof *debug_data;++debug_i){
        x = debug_data[debug_i];
#endif
    if (x==5)        foo1();
    else if (x==10)  foo2();
    else             foo3();
#ifdef DEBUG
    }
#endif

    return 0;
}
于 2013-07-11T11:04:16.750 回答
0

您基本上是在询问所谓的分析(每个函数被调用多少次)和代码覆盖率(遍历代码的每个分支)。如果您正在使用gcc,您应该查看-p分析--coverage选项和代码覆盖选项。这两个选项都将代码添加到程序中,以便在运行程序时进行测量。然后使用该程序gprof查看分析结果,并使用该程序gcov查看代码覆盖率。

我不完全理解您为什么要更改原始程序。上述方法依赖于精心构建的程序测试输入,以提供具有代表性的执行(用于分析)和高代码覆盖率。在您的示例程序中,编译器可能会优化对 的调用foo2foo3因为没有可能的程序输入会触发这些调用。相反,如果您的程序看起来像:

#include <stdio.h>

void foo1(void)
{
    printf("Value is 5");
}

void foo2(void)
{
    printf("Number is 10");
}

void foo3(void)
{
    printf("Number is neither 10 nor 5");
}

int main (int argc, char ** argv)
{
    switch (argc)
    {
    case 5: foo1(); break;
    case 10: foo2(); break;
    default: foo3(); break;
    }

    return 0;
}

然后将程序运行为a.out 1,a.out 1 2 3 4并使a.out 1 2 3 4 5 6 7 8 9程序通过所有分支。这三个运行将全面覆盖该程序。

应该注意的是,分析和代码覆盖本身就是一门艺术。在生成测试集以获得高代码覆盖率方面已经进行了大量研究,但我认为没有广泛建立的工具来生成这样的测试集。主要是因为它不是微不足道的。然而,手动制作具有高代码覆盖率的测试集是软件测试中的标准程序。

于 2013-07-11T08:41:47.793 回答
0

您也可以通过这种方式更改您的main()功能

int main (void)
{
    int x = 5;
    while () {
       if (x==5)        { foo1(); x=10;}
       else if (x==10)  { foo2(); x=0; }
       else             { foo3(); break;}
    }
    return 0;
}
于 2013-07-11T08:06:15.523 回答