Chinaunix

标题: [已解决]backtrace在子进程中解析不了符号信息 [打印本页]

作者: dreamtale90    时间: 2017-06-28 11:39
标题: [已解决]backtrace在子进程中解析不了符号信息
本帖最后由 dreamtale90 于 2017-07-12 08:55 编辑

有个程序要以子进程的方式启动,但是这种方式下出现段错误的时候,backtrace这组函数并没有解析出相应的符号信息,如果直接执行该程序,就能够解析出符号信息,图片中是写的小demo,fork_child程序会以vfork的方式执行backtrace程序,backtrace程序中故意制造了除零错误。求解答。


作者: dreamtale90    时间: 2017-06-28 11:57
本帖最后由 dreamtale90 于 2017-06-28 12:04 编辑

图好像挂了,重新上传一个,另外补充一下,编译时候加了rdynamic选项。
[attach]702157[/attach]

作者: dreamtale90    时间: 2017-06-28 13:49
附上demo的代码,这是backtrace.c
  1. #include <execinfo.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <signal.h>

  6. const char *SigInfo[][8] = {
  7.     {
  8.         "SIGSEGV: Address not mapped to object.",
  9.         "SIGSEGV: Invalid permissions for mapped object.",
  10.     },
  11.     {
  12.         "SIGFPE: Integer divide by zero.",
  13.         "SIGFPE: Integer overflow.",
  14.         "SIGFPE: Floating point divide by zero.",
  15.         "SIGFPE: Floating point overflow.",
  16.         "SIGFPE: Floating point underflow.",
  17.         "SIGFPE: Floating point inexact result.",
  18.         "SIGFPE: Floating point invalid operation.",
  19.         "SIGFPE: Subscript out of range.",
  20.     },
  21. };

  22. const char *GetSigCodeInfo(int signo, int code)
  23. {
  24.     int sigIndex = 0, codeIndex = code - 1;

  25.     if(signo == SIGSEGV)
  26.     {   
  27.         sigIndex = 0;
  28.     }   
  29.     else if(signo == SIGFPE)
  30.     {   
  31.         sigIndex = 1;
  32.     }   
  33.     else
  34.     {   
  35.         return "Error signal info.";
  36.     }   

  37.     return SigInfo[sigIndex][codeIndex];
  38. }

  39. void CatchSignalHandler(int n, siginfo_t *siginfo, void *myact)
  40. {
  41.     void *array[100];
  42.     int i = 0;
  43.     int num = backtrace(array, sizeof(array)/sizeof(array[0]));
  44.     char **calls = backtrace_symbols(array, num);
  45.    
  46.     printf("%s Fault address:%p\n", GetSigCodeInfo(siginfo->si_signo, siginfo->si_code), siginfo->si_addr);
  47.     for (i = num - 1; i > 1; i--)
  48.     {   
  49.         printf("%s\n", calls[i]);
  50.     }   

  51.     exit(1);
  52. }

  53. void func(void)
  54. {
  55.     int a, b, c = 0;

  56.     a = b / c;
  57. }


  58. int main()
  59. {
  60.     /* Init Signal Process */
  61. #if     1
  62.     struct sigaction act;
  63.     sigemptyset(&act.sa_mask);
  64.     sigaddset(&act.sa_mask, SIGSEGV);
  65.     sigaddset(&act.sa_mask, SIGFPE);
  66.     act.sa_sigaction = CatchSignalHandler;
  67.     act.sa_flags = SA_SIGINFO;
  68.     sigaction(SIGSEGV, &act, NULL);
  69.     sigaction(SIGFPE, &act, NULL);
  70. #endif

  71.     func();
  72.     return 0;
  73. }
复制代码
下面是fork_child.c
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <limits.h>
  5. #include <errno.h>
  6.    
  7.    
  8. int StartTask(const char *path)
  9. {   
  10.     int ret = 0;
  11.    
  12.     pid_t pid = vfork();
  13.     if(pid == -1)
  14.     {
  15.         perror("vfork");
  16.         ret = -1;
  17.     }   
  18.     //start task
  19.     else if(pid == 0)
  20.     {
  21.         execl(path, NULL);
  22.         exit(0);
  23.     }
  24.     else
  25.     {
  26.         printf("start task %s, pid is %d\n", path, pid);
  27.     }
  28.    
  29.     return ret;
  30. }   
  31.    
  32. int main(int argc, char **argv)
  33. {   
  34.     if(argc != 2){
  35.         printf("%s child\n", argv[9]);
  36.         return -1;
  37.     }

  38.     StartTask(argv[1]);
  39.    
  40.     sleep(1);
  41.    
  42.     return 0;
  43. }
复制代码


作者: wlmqgzm    时间: 2017-06-28 20:13
本帖最后由 wlmqgzm 于 2017-06-28 20:26 编辑

我这边做  backtrace  gcc中添加下列选项  -g选项, -rdynamic选项



signal( SIGSEGV, signal_crash_handler );
   signal( SIGABRT, signal_crash_handler );

void signal_crash_handler( int signum )
{
  char  chars_tmp[24];
  std::string str_msg;
  try   {
    str_msg.reserve( 256 );
    str_msg = "#########################################################\n";
    str_msg += " crash signal number:";
    str_msg += haisql::int_to_chars( signum, chars_tmp );
    std::cout << str_msg << std::endl;
   
    void* array1[MAX_STACK_FRAMES];
    char** char_strings = nullptr;
    signal( signum, SIG_DFL );
    size_t  size1 = backtrace( array1, MAX_STACK_FRAMES );
    char_strings = (char**)backtrace_symbols( array1, size1 );
    for ( size_t i = 0; i < size1; ++i )    {
      str_msg = haisql::uint_to_chars( i, chars_tmp );
      str_msg += " ";
      str_msg += char_strings;
      str_msg += "\r\n";
      std::cout << str_msg << std::endl;
      }
    if( char_strings !=nullptr )  {
      free( char_strings );
      char_strings = nullptr;
      }
    }
  catch (...)   {
    }

  exit( -1 );
  return;
}

作者: dreamtale90    时间: 2017-06-28 21:11
回复 4# wlmqgzm

很感谢你的回复,-g和-rdynamic我都加过,但还是一样。
我觉得我这个问题应该是vfork+execl导致的,
我这样试过,把子进程backtrace的main改个名字,然后父进程在vfork后直接调用子进程backtrace,这样可以正常解析出符号信息
感觉像是vfork+execl这种方式致使子进程没有加载自己的符号信息,或者其他什么?

作者: nswcfd    时间: 2017-07-10 16:00
本帖最后由 nswcfd 于 2017-07-10 16:23 编辑

跟在sigstack上调用bt有关系么?

[更新]已验证,没有关系。
跟fork/vfork没有关系,跟exec有关。
调用的时候没有提供arg0。


作者: dreamtale90    时间: 2017-07-12 08:49
nswcfd 发表于 2017-07-10 16:00
跟在sigstack上调用bt有关系么?

[更新]已验证,没有关系。

太感谢了,我后来也发现是execl的问题了,就是没想明白符号信息为什么没有装载进来。一直以来的疑惑终于解开了。3Q





欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2