注意:我意识到我的问题不明确。我现在修改了它,并为首先犯错而道歉。
我有一个大型 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()。
是有意义的运行整体代码的任何想法/工具?
我认为修改主要功能的方法之一是:
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;
}
这种方式贯穿原始代码。 知道如何进行这种修改吗?
答案 0 :(得分: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;
}
答案 1 :(得分: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;
}
答案 2 :(得分:0)
您基本上要求的是所谓的 profiling (每个函数被调用多少次)和代码覆盖(遍历代码的每个分支)。如果您使用gcc
,则应查看用于分析的-p
选项和用于代码覆盖的--coverage
选项。这两个选项都会向程序添加代码,以便在运行程序时进行测量。然后,程序gprof
用于查看分析结果,程序gcov
用于查看代码覆盖率。
我不完全理解您为什么要更改原始程序。上述方法依赖于精心构造的程序测试输入,以提供代表性执行(用于分析)和高代码覆盖。在您的示例程序中,编译器可能会优化对foo2
和foo3
的调用,因为没有可能的程序输入会触发这些调用。相反,如果您的程序看起来像:
#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
运行该程序将使程序通过所有分支。这三次运行将完全覆盖该计划。
应该注意的是,分析和代码覆盖本身就是一门艺术。在生成测试集以获得高代码覆盖率方面已经进行了大量研究,但我认为没有广泛建立的工具来生成这样的测试集。主要是因为做起来并不容易。然而,手动制作提供高代码覆盖率的测试集是软件测试中的标准过程。