kaka1902 发表于 2013-09-06 17:20

打印调用栈的问题

本帖最后由 kaka1902 于 2013-09-06 17:21 编辑

最近遇到一个难题:
在代码crash的时候,我有一段代码会捕获信号后打印调用栈:
调用栈是采用__build_frame_address()来获取当前调用地址的
{
int          n;
unsigned int fpadd, fpaddnext;
unsigned int stackadd = (unsigned int)&fpadd;

fpadd = (unsigned int)__builtin_frame_address(1);

for ( n = 0; n < nb; n++ )
    {
   
    tab    = (void *)*(((unsigned int *)fpadd) + 1);
    fpaddnext = *(unsigned int *)fpadd;

    if ( (fpaddnext <= fpadd) || (fpaddnext > (stackadd + 0x10000)))
      {
      return (n + 1); // Next fp is outside the stack or too far
      }
    fpadd = fpaddnext;
    }
}

但是我测试了一下发现每次只能打一级调用的函数,再往下就没有了

这个问题可能跟什么相关?麻烦各位指点一下!

kaka1902 发表于 2013-09-06 17:22

补充一下
编译环境是ARM的交叉编译工具链?
有没有可能是工具链带的libc库编译选项指定的不对?

idi0t 发表于 2013-09-06 23:48

不懂也,不知道lZ的环境里有没有backtrace可以用

wps103 发表于 2013-09-07 11:33

试过backtrace(), 也是打不出来东西
感觉libc的栈都无法打印,难不成真是工具链提供libc编译的时候没有添加-rdynamic -ldl?

井蛙夏虫 发表于 2013-09-07 12:03

本帖最后由 井蛙夏虫 于 2013-09-07 12:03 编辑

回复 4# wps103
posix标准保证能在信号处理函数中安全调用的函数没有__builtin_frame_address和backtrace,所以这些是未定义行为。(man 7 signal)

wps103 发表于 2013-09-08 10:43

不在信号处理函数里面实现
做了一个实验,在宿主机上和ARM上分别测试一下代码:
宿主机:ubuntu#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
void fun1(void);
void fun2(void);
void fun3(void);
void print_stacktrace(void);


int main(void)
{
    fun3();
    return 0;
}
void fun1()
{
    fprintf(stderr,"stackstrace begin:\n");
    print_stacktrace();
}
void fun2()
{
    fun1();
}
void fun3()
{
    fun2();
}
void print_stacktrace()
{
    int size = 16;
    void * array;
    int i=0;
    int stack_num = backtrace(array, size);
    char ** stacktrace = backtrace_symbols(array, stack_num);
    for (i = 0; i < stack_num; ++i)
    {
      fprintf(stderr,"%s\n", stacktrace);
    }
    free(stacktrace);
}
编译:
宿主机:
gcc backtrace.c -rdynamic -g -o backtrace
交叉编译:
arm-linux-cc backtrace.c -rdynamic -g -o backtrace

运行结果:
宿主机:
stackstrace begin:
./backtrace(print_stacktrace+0x26)
./backtrace(fun1+0x33)
./backtrace(fun2+0xb)
./backtrace(fun3+0xb)
./backtrace(main+0xb)
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)
./backtrace()


ARM:
stackstrace begin:
/lib/ld-linux.so.3

看起来跟安全性没什么关系,可能还是编译选项的问题?
大牛们请再指教!

myworkstation 发表于 2013-09-09 15:30

回复 1# kaka1902


    和你传的参数1有关吧。只处理一层的frame stack。__builtin_frame_address(1)。改大点试试

wps103 发表于 2013-09-10 14:16

回复 7# myworkstation

This function is similar to __builtin_return_address, but it returns the address of the function frame rather than the return address of the function. Calling __builtin_frame_address with a value of 0 yields the frame address of the current function, a value of 1 yields the frame address of the caller of the current function, and so forth.

The frame is the area on the stack which holds local variables and saved registers. The frame address is normally the address of the first word pushed on to the stack by the function. However, the exact definition depends upon the processor and the calling convention. On the Motorola 68000, if the function has a frame, then __builtin_frame_address will return the value of the frame pointer register a6 if level is 0.

This function should be used with a nonzero argument only for debugging purposes.

从解释来看,入参并不是指定需要获取多少个stack frame,而是指定获取第几级的调用stack frame
因此改大应该也没有什么好处,不过我可以试下认为多取几级堆栈,看看究竟有没有东西


   

井蛙夏虫 发表于 2013-09-10 18:41

回复 8# wps103
x86平台能打印,你改改试试#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <execinfo.h>

#define BACKTRACE_MAXDEPTH 32

struct layout               // 来自glibc-2.16.0/sysdeps/generic
{
      void *__unbounded next;
      void *__unbounded return_address;
};

int my_backtrace(void **arr, int maxsize)
{
    int cnt = 0;
    void *fp = __builtin_frame_address(0);
    struct layout *lp = (struct layout *)fp;
    while (cnt < maxsize)
    {

      arr = lp->return_address;
      if (!lp->next)
      {
            break;
      }
      lp = (struct layout*)lp->next;
    }
    return cnt;
}


void my_backtrace_symbols(void* const* arr, int size)
{
    for (int i = 1; i < size; ++i)
    {
      Dl_info info;
      if (dladdr(arr, &info))
            fprintf(stderr, "%s(%s+0x%x) [%p]\n", info.dli_fname, info.dli_sname, (int)arr - (int)info.dli_saddr, arr);
      else
            fprintf(stderr, "[%p]\n", arr);
    }
}

void print_my_stacktrace()
{
    void * array;
    int i=0;
    int stack_num = my_backtrace(array, BACKTRACE_MAXDEPTH);
    char ** stacktrace = backtrace_symbols(array, stack_num);
    for (i = 0; i < stack_num; ++i)
    {
      fprintf(stderr,"%s\n", stacktrace);
    }
    free(stacktrace);
}

void fun1()
{
    fprintf(stderr,"stackstrace begin:\n");
    print_my_stacktrace();
}

void fun2()
{
    fun1();
}

void fun3()
{
    fun2();
}

int main(void)
{
    fun3();
    return 0;
}
编译指令gcc print_backtrace.c -ldl -rdynamic -D_GNU_SOURCE -std=gnu99 -Wall结果stackstrace begin:
./a.out(print_my_stacktrace+0x26)
./a.out(fun1+0x33)
./a.out(fun2+0xb)
./a.out(fun3+0xb)
./a.out(main+0xb)
/lib/libc.so.6(__libc_start_main+0xf3)
页: [1]
查看完整版本: 打印调用栈的问题