C语言中的函数调用序列

时间:2012-03-16 23:35:17

标签: c debugging gcc

我想看一下基本程序的代码流程,如下所示。

#include<stdio.h>
int main ()
{
    FILE *fptr = fopen("/mnt/myfilesystem/test.txt", "r");
    if(fptr) {
        printf("open successful\n");
        fclose(fptr);
    }
    else
        printf("open failed\n");

    return 0;
}

我希望看到运行此程序时执行的完整函数调用序列。 我尝试使用strace和ptrace,但他们只列出系统调用:( 关于如何做到这一点的任何建议?

提前致谢!!

6 个答案:

答案 0 :(得分:5)

您可以使用gnu profiler gprof

使用-pg标志编译它(main.cc):

gcc -pg main.cc -o main

然后执行它(./main)。您的二进制文件将生成一个文件(gmon.out)。然后,您可以使用gprof:

获取跟踪

gprof main gmon.out

我的例子我得到了:

index % time    self  children    called     name
                0.00    0.00       1/1           __do_global_ctors_aux [9]
[7]      0.0    0.00    0.00       1         global constructors keyed to main [7]
                0.00    0.00       1/1           __static_initialization_and_destruction_0(int, int) [8]
-----------------------------------------------
                0.00    0.00       1/1           global constructors keyed to main [7]
[8]      0.0    0.00    0.00       1         __static_initialization_and_destruction_0(int, int) [8]

这个信息不多,因为您的应用程序并不复杂。

如果要跟踪系统调用,请尝试strace ./main。输出很长,您将看到所有系统调用:

execve("", [""], [/* 26 vars */]) = 0
brk(0)                                  = 0x877f000
access("", F_OK)      = -1 ENOENT
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7748000
access("", R_OK)      = -1 ENOENT
open("", O_RDONLY) = -1 ENOENT
open("", O_RDONLY) = -1 ENOENT
stat64("", 0xbfdc4a18) = -1 ENOENT
open("", O_RDONLY) = -1 ENOENT
stat64("", {st_mode=S_IFDIR|0755, st_size=6104, ...}) = 0
open("", O_RDONLY) = -1 ENOENT
open("", O_RDONLY)      = 3
....

答案 1 :(得分:3)

听起来你想要ltrace

答案 2 :(得分:2)

这不是一个已经回答的呼叫追踪,但我认为该代码包含一个错误,这可能是你问的原因。

close应为fclose,以匹配fopen

close用于文件描述符,而不是由FILE*

返回的fopen

答案 3 :(得分:0)

您是否可以访问Mac,Solaris或FreeBSD计算机?

他们都有DTrace,通过使用适当的提供程序,您可以追踪任何您想要的东西。如果您可以访问具有其中一个操作系统的计算机,请查看dtrace用户指南(最好找到可下载的pdf)

这可能会为您提供一个脚本,它可以提供程序跟踪:
http://www.dtracebook.com/index.php/Applications
要么 http://www.brendangregg.com/dtrace.html

答案 4 :(得分:0)

静态编译和反汇编。

答案 5 :(得分:0)

编译进行性能分析时,使用-pg编译器会自动插入对名为mcount的函数的调用。此函数由特殊库提供,并创建所有调用的数据库(具有from-address和to-address以及计数器)。

但是,如果你提供自己的mcount函数(小心来编译启用了性能分析!)它可以做你喜欢的任何事情。

我没有测试过这个,我真的不知道mcount参数是什么(虽然我确定它不是很难找到)但是示例实现可能如下所示:

void mcount (void *to)
{
  void *from = __builtin_return_address ();
  printf ("call from %p to %p\n", from, to);
}

然后,您可以使用addr2line来确定哪个函数包含哪个地址。

但是存在一个问题:它只计算已启用分析编译的函数,而可能不包含C库函数,例如printf。

但是,如果您的系统确实启用了配置文件C库,那么您必须非常小心地调用可能会调用mcount的函数,否则您将得到一个无限循环。一种解决方案是直接使用系统调用手动输出文本,也可以用asm编写。另一种解决方案是检测递归调用并返回而不做任何事情,但这在多线程应用程序中甚至是信号处理程序中都会出现问题。