2014-01-23 5 views
3

내 C에 대한 간단한 충돌 로거를 구현하고있어 ++ 응용 프로그램 발생했습니다 매우 기능 인쇄 실패backtrace_symbols는 신호

static void handler(int, siginfo_t * info, void *) { 
    void *array[1000]; 

    switch (info->si_signo) { 
    case SIGILL: 
     Logger() << "Received SIGILL"; 
     break; 
    case SIGSEGV: 
     Logger() << "Received SIGSEGV"; 
     break; 
    case SIGBUS: 
     Logger() << "Received SIGBUS"; 
     break; 
    case SIGSYS: 
     Logger() << "Received SIGSYS"; 
     break; 
    default: 
     break; 
    } 

    // get void*'s for all entries on the stack 
    const size_t size = backtrace(array, 1000); 

    // print out all the frames 
    char ** symbols = backtrace_symbols(array, size); 
    for(size_t i = 0; i < size; ++i) 
    { 
     Logger() << symbols[i]; 
    } 

    free(symbols); 
    exit(EXIT_FAILURE); 
} 

struct sigaction SignalAction; 
SignalAction.sa_flags = SA_SIGINFO; 
SignalAction.sa_sigaction = handler; 
sigemptyset(&SignalAction.sa_mask); 
sigaction(SIGSEGV, &SignalAction, NULL); 
sigaction(SIGBUS, &SignalAction, NULL); 
sigaction(SIGILL, &SignalAction, NULL); 

나는 아직 리눅스에 그것을 테스트하지 않은,하지만 OS X에 내가 관심이있는 특정 추적 항목, 신호를 발생했습니다 thatr 하나이며, 인쇄되지 (항목 번호 2) :

: " Received SIGSEGV" 
: " 0 App      0x0000000100253d15 _ZL7handleriP9__siginfoPv + 229" 
: " 1 libsystem_platform.dylib 0x00007fff8ff0f5aa _sigtramp + 26" 
: " 2 ???      0x000000000000000c 0x0 + 12" 
: " 3 App      0x000000010000dfa7 _ZN11CMainWindow13initShortcutsEv + 231" 
: " 4 App      0x000000010000d059 _ZN11CMainWindowC2EP7QWidget + 1001" 
: " 5 App      0x00000001000091d9 main + 6217" 
: " 6 App      0x00000001000070a5 _start + 227" 
: " 7 App      0x0000000100006fc1 start + 33" 

왜 이런 일이, 내가이 문제를 해결할 수 있습니까?

P. S. 이것은 디버그 빌드입니다. 실제 segfault는 없었으며 raise(SIGSEGV)으로 시뮬레이션되었습니다. raiseMainWindow::initShortcuts에서 호출 된 메서드에서 호출되었습니다.

+0

출력이 어떻게 될 것으로 예상합니까?이 추적이 잘못되었음을 어떻게 알 수 있습니까?스택 손상이 발생했거나 (백 트레이스를 엉망으로 만들었을 때) 프로세스가 0xC 주소로 점프하려고 시도했을 가능성이 있습니다 (메모리 손상, 프로그래밍 오류 등으로 인해). –

+0

글쎄, C++을 사용하고 있기 때문에'std :: exception'에서 파생 될 수 있고 파생 된 예외의 생성자에서'std :: string'의'std :: list'에 스택 추적 정보를 빌드 할 수 있습니다 그 파생 된 클래스를 던지십시오. 대답이 표시되면 정확한 스택 추적을 제공하기 위해 신호 처리기에 의존해서는 안됩니다. – rwols

+0

@AndrewMedico : 업데이트 된 질문 (바닥 글 - P. 섹션 참조)을 참조하십시오. –

답변

3

엔트리 번호 2에는 매우 특별한 주소가 있습니다. 0x000000000000000c이고 인터럽트 설명 테이블이 있다는 것을 알아야합니다. http://en.wikipedia.org/wiki/Interrupt_Descriptor_Table에 따르면 스택 오류 일 수 있습니다.

그러나 Ken이 지적한 것처럼,하고있는 일을하는 것은 나쁩니다. 그것은 대동맥이 찢어 질 때 당신을 때리는 자동차의 번호판을 기억하려고하는 것과 같습니다. 당신은 할 수 있지만 무엇을 위해서입니까?

아마도 대신 strace http://en.wikipedia.org/wiki/Strace을 사용하는 방법을 배우십시오.

+0

Strace가 필요한 것을 수행하지 않는 것 같습니다. –

+0

흠,이 추적 로그를 이해할 때, Stack Fault가': "App 0x000000010000dfa7 _ZN11CMainWindow13initShortcutsEv + 231"'에 의해 발생한 것으로, 프로세서가 인터럽트 테이블 (항목 2)을 찾아보고 libsystem_platform.dylib에서이 신호를 처리하는 절차로 넘어갔습니다. (항목 1) 자신의 신호 처리기를 설정했기 때문에 코드 (항목 0)를 다시 호출했습니다. IDT는 핸들러를 인터럽트하기위한 점프 명령어 목록입니다. – omikron

+0

스택 오류가 없습니다. 'SIGSEGV' 신호가있었습니다. 그리고 그것은'ZN11CMainWindow13initShortcutsEv'에 의해서가 아니라 다른 곳에서 호출되었습니다. –

3

신호 처리기로 크래시 로거를 작성한다는 아이디어에는 근본적인 결함이 있습니다. sigaction man page의 "참고"절에서 :

다음의 기능 중 하나 또는 재진입 신호에 의해 인터럽트되지 않고 비동기 신호 안전하다. 따라서 애플리케이션은 신호 끄는 기능에서, 제한없이, 에게 그들의 호출 수

... 비동기 신호 안전 기능리스트 생략 ...]

상기 목록에 대하여 위험한 것으로 간주되지 모든 기능 신호로. 즉, 신호 처리기에서 호출 할 때 이러한 함수의 동작은 정의되지 않습니다. 그러나 일반적으로 신호 처리기는 플래그를 설정하는 것 이상을 수행하지 않아야합니다. 대부분의 다른 작업은 안전하지 않습니다.

없음 backtrace(), backtrace_symbols(), free() 또는 exit()는 신호 처리기에서 호출하는 것이 안전하다. 거의 확실하게 귀하의 Logger 또는 operator<<에있는 내용도 안전하지 않습니다.

신호 발생 기능이 백 트레이스에없는 이유는 분명히 신호 처리기 컨텍스트가 커널에 의해 설정되는 방법 때문입니다. 백 트레이스는 호출 문과 함수 프롤로그로 구성된 스택 프레임을 검사하여 얻습니다. 시그널 핸들러는 일반적인 함수 호출로 호출되지 않으므로, 스택 프레임 체인이 올바르게 설정 될 것이라는 특별한 이유는 없습니다.

실제로 신호 처리기는 일반 코드와 완전히 다른 스택에서 실행되도록 만들 수 있습니다. sigaltstack()을 참조하십시오.