2012-05-12 2 views
1

신호 처리기로 오류를 catch 한 다음 스택 추적 정보를 인쇄하여 오류 보고서 및 디버깅을 위해 로그 파일 (또는 콘솔)에 추가하려고합니다. 비 개발 기계. 내 문제는 때로는 전체 스택 프레임 역 추적을 얻지 못한다는 것입니다. 매달리지 않고 끝내거나 끝내지 않는 경우가 많습니다. 때로는 성공적으로 종료됩니다.snprintf에서 strlen을 호출하는 중 backtrace_symbols_fd()를 호출하는 경우가 종종 발생합니다.

#include <signal.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <execinfo.h> 

typedef struct { char name[10]; int id; char description[40]; } signal_def; 

signal_def signal_data[] = 
{ 
    { "SIGHUP", SIGHUP, "Hangup (POSIX)" }, 
    { "SIGINT", SIGINT, "Interrupt (ANSI)" }, 
    { "SIGQUIT", SIGQUIT, "Quit (POSIX)" }, 
    { "SIGILL", SIGILL, "Illegal instruction (ANSI)" }, 
    { "SIGTRAP", SIGTRAP, "Trace trap (POSIX)" }, 
    { "SIGABRT", SIGABRT, "Abort (ANSI)" }, 
    { "SIGIOT", SIGIOT, "IOT trap (4.2 BSD)" }, 
    { "SIGBUS", SIGBUS, "BUS error (4.2 BSD)" }, 
    { "SIGFPE", SIGFPE, "Floating-point exception (ANSI)" }, 
    { "SIGKILL", SIGKILL, "Kill, unblockable (POSIX)" }, 
    { "SIGUSR1", SIGUSR1, "User-defined signal 1 (POSIX)" }, 
    { "SIGSEGV", SIGSEGV, "Segmentation violation (ANSI)" }, 
    { "SIGUSR2", SIGUSR2, "User-defined signal 2 (POSIX)" }, 
    { "SIGPIPE", SIGPIPE, "Broken pipe (POSIX)" }, 
    { "SIGALRM", SIGALRM, "Alarm clock (POSIX)" }, 
    { "SIGTERM", SIGTERM, "Termination (ANSI)" }, 
    //{ "SIGSTKFLT", SIGSTKFLT, "Stack fault" }, 
    { "SIGCHLD", SIGCHLD, "Child status has changed (POSIX)" }, 
    //{ "SIGCLD", SIGCLD, "Same as SIGCHLD (System V)" }, 
    { "SIGCONT", SIGCONT, "Continue (POSIX)" }, 
    { "SIGSTOP", SIGSTOP, "Stop, unblockable (POSIX)" }, 
    { "SIGTSTP", SIGTSTP, "Keyboard stop (POSIX)" }, 
    { "SIGTTIN", SIGTTIN, "Background read from tty (POSIX)" }, 
    { "SIGTTOU", SIGTTOU, "Background write to tty (POSIX)" }, 
    { "SIGURG", SIGURG, "Urgent condition on socket (4.2 BSD)" }, 
    { "SIGXCPU", SIGXCPU, "CPU limit exceeded (4.2 BSD)" }, 
    { "SIGXFSZ", SIGXFSZ, "File size limit exceeded (4.2 BSD)" }, 
    { "SIGVTALRM", SIGVTALRM, "Virtual alarm clock (4.2 BSD)" }, 
    { "SIGPROF", SIGPROF, "Profiling alarm clock (4.2 BSD)" }, 
    { "SIGWINCH", SIGWINCH, "Window size change (4.3 BSD, Sun)" }, 
    { "SIGIO", SIGIO, "I/O now possible (4.2 BSD)" }, 
    //{ "SIGPOLL", SIGPOLL, "Pollable event occurred (System V)" }, 
    //{ "SIGPWR", SIGPWR, "Power failure restart (System V)" }, 
    { "SIGSYS", SIGSYS, "Bad system call" }, 
}; 

void bt_sighandler(int sig, siginfo_t *info, void *secret) { 
    signal_def *sigd = NULL; 
     for (int i = 0; i < sizeof(signal_data)/sizeof(signal_def); ++i) { 
      if (sig == signal_data[i].id) { 
      sigd = &signal_data[i]; 
      break; 
      } 
     } 
    //ucontext_t* uc = (ucontext_t*) secret; 
    //void *pnt = (void*) uc->uc_mcontext.gregs[REG_RIP] ; 

    void *trace[16]; 
    int trace_size = backtrace(trace, 16); 
    /* overwrite sigaction with caller's address */ 
    //trace[1] = pnt; 

    if (sigd) { 
     fprintf(stderr, "SigHandler(0x%02X)[%d]:%s[%s]", sig, trace_size, 
      sigd->name, sigd->description); 
     } else { 
     fprintf(stderr, "SigHandler(0x%02X)[%d]", sig, trace_size); 
     } 

    backtrace_symbols_fd(trace, trace_size, fileno(stderr)); 

    exit(1); 
} 

#endif 

int main(int argc, char* argv[]) { 
    struct sigaction sa; 

    sa.sa_sigaction = bt_sighandler; 
    sigemptyset(&sa.sa_mask); 
    sa.sa_flags = 0; 

    sigaction(SIGINT, &sa, NULL); 
    sigaction(SIGSEGV, &sa, NULL); 
    sigaction(SIGBUS, &sa, NULL); 
    sigaction(SIGILL, &sa, NULL); 
    sigaction(SIGFPE, &sa, NULL); 
    sigaction(SIGUSR1, &sa, NULL); 
    sigaction(SIGUSR2, &sa, NULL); 

    signal(SIGPIPE, SIG_IGN); 

    //Produce a fault 

    return 0; 
} 

당신은 발신자의 주소를 가진은 sigaction을 덮어을 담당하는 부분은 주석 처리 된 내 샘플 코드에서 알 수 있습니다 :

여기 내 코드입니다. Mac 용으로 컴파일하는 방법을 모르기 때문입니다. 당신은 단지 처음 3 개 프레임을 인쇄 한 후 9 개 프레임이 발견 인쇄하기로했다 비록 종료하지 않고 걸린 것을 알 수 있습니다 console output http://www.minesclubtennis.com/images/stackoverflow/fatalconsoleoutputhang.png

: 여기

은 샘플 콘솔 출력됩니다.

그래서 Activity Monitor 응용 프로그램에서 "샘플 프로세스"를 수행하여 backtrace_symbols_fd 기능을 실행하는 스레드가 strlen에 걸렸음을 발견했습니다. 스크린 샷 : sample process output http://www.minesclubtennis.com/images/stackoverflow/sampleprocessoutputhang.png

왜 걸려 있습니까? 이것은 내 코드의 버그입니까, 아니면 Apple backtrace의 버그입니까? 나는 신호 처리기로 할 수있는 제한된 것들이 있다고 들었지만, 나는 무엇이 잘못되었는지를 나타내는 sigaction man page에 아무것도 표시하지 않는다.

답변

3

sigaction 매뉴얼 페이지를 더 자세히 읽어야합니다! 시그널 세이프리스트에없는 것들은 시그널 핸들러에서 verboten입니다. backtrace_symbols_fd()가 해당 목록에 없습니다. 신호 처리기에서는 사용할 수 없습니다.

정확한 이유를 보려면 Apple의 오픈 소스 사이트로 이동하여 Libc 코드를 다운로드하십시오. 캡쳐는 문제가 어디에 있는지를 보여줍니다. 당신이 "표준 입출력/vprintf - fbsd.c"를 보면 당신은()이 __vfprintf 볼이 댓글을 보유하고 있습니다 : printf와 스타일 기능

/* 
* Non-MT-safe version 
*/ 

많은이 여기까지 (현재 snprintf 우리가 여기에 도착하는 방법이다). 응용 프로그램이 printf 스타일 함수에서 크래시되고 신호 처리기가 다시 입력하려고하면 예상치 못한 동작이 예상됩니다.

또는 앱이 printf 스타일 함수에서 중단되지 않지만 다른 일부 스레드가 충돌 할 때 printf 스타일 함수에있는 경우에도 이러한 동작을 볼 수 있습니다.

+0

통찰력에 감사드립니다. 외관상으로는 다른 사람은이 기술을 사용하여 신호 처리기에서 백 트레이스를 생성 할 수 있다고 생각하기 때문에 일반적인 오해입니다. 내가 발견 한이 비디오 강연은 문제를 요약하고 해결책을 제시합니다. http://free-electrons.com/pub/video/2008/ols/ols2008-gilad-ben-yossef-fault-handlers.ogg 그의 강의에 설명 된 샘플 코드 프로젝트는 다음과 같습니다. https://github.com/gby/libcrash 샘플 코드와 plcrashreporter를 기반으로하는 자체 솔루션을 만들려고합니다. – BigMacAttack

관련 문제