2011-03-22 2 views
1

스택 추적 출력을 stderr로 가져 오거나 로그 파일로 덤프하는 데 문제가 있습니다. gcc 컴파일러 (4.4.3)를 사용하여 쿠분투 10.04에서 코드를 실행하고 있습니다. 문제는 정상적인 실행 모드 (gdb 제외)에서 'Segmentation Fault'를 제외하고 아무것도 출력하지 않는다는 것입니다. 아래의 print 문과 같이 백 트레이스 출력을 출력하고 싶습니다.gcc의 SIGSEGV 중에 C++ 프로그램이 함수 호출이나 printf를 처리하지 않습니다

669  { 
(gdb) 
670  printf("Testing for stability.\n"); 
(gdb) 

Program received signal SIGTRAP, Trace/breakpoint trap. 
0x00007ffff68b1f45 in puts() from /lib/libc.so.6 

이상한 일들이 내가 내에서 함수를 호출하는 경우 작동한다는 것입니다 : 내 응용 프로그램과 함께 GDB를 실행하면 선언문의 printf/fprintf와/(함수 호출)에 제공하고 다음 문 충돌 충돌하는 동일한 파일, 그것은 잘 작동하고 제대로 출력 spews. 그러나이 파일 외부의 함수에서 프로그램이 중단되면 출력을 인쇄하지 않습니다. printf 또는 파일 덤프 문이나 함수 호출이 처리되지 않습니다. 다음 샘플 코드를 사용하고 있습니다 :

void bt_sighandler(int sig, siginfo_t *info, 
       void *secret) { 

void *trace[16]; 
char **messages = (char **)NULL; 
int i, trace_size = 0; 
ucontext_t *uc = (ucontext_t *)secret; 

/* Do something useful with siginfo_t */ 
if (sig == SIGSEGV) 
    printf("Got signal %d, faulty address is %p, " 
     "from %p\n", sig, info->si_addr, 
     uc->uc_mcontext.gregs[0]); 
else 
    printf("Got signal %d#92; \n", sig); 

trace_size = backtrace(trace, 16); 
/* overwrite sigaction with caller's address */ 
trace[1] = (void *) uc->uc_mcontext.gregs[0]; 

messages = backtrace_symbols(trace, trace_size); 
/* skip first stack frame (points here) */ 
printf("[bt] Execution path:#92; \n"); 
for (i=1; i<trace_size; ++i) 
    printf("[bt] %s#92; \n", messages[i]); 

exit(0); 
} 


int main() { 

/* Install our signal handler */ 
struct sigaction sa; 

sa.sa_sigaction = (void *)bt_sighandler; 
sigemptyset (&sa.sa_mask); 
sa.sa_flags = SA_RESTART | SA_SIGINFO; 

sigaction(SIGSEGV, &sa, NULL); 
sigaction(SIGUSR1, &sa, NULL); 
/* Do something */ 
printf("%d#92; \n", func_b()); 
} 

미리 도움을 청하십시오.

답변

2

당신은 시그널 핸들러에서 거의 아무것도하지 않아야한다. 원칙적으로 sig_atomic_t 타입의 변수와 휘발성 데이터에만 접근한다.

I/O를 수행하는 것은 확실히 의문의 여지가 없습니다. GCC이 페이지를 참조하십시오

http://www.gnu.org/s/libc/manual/html_node/Nonreentrancy.html#Nonreentrancy

+0

하지만이 스레드에서 전처리 기의 출력을 생성하는 사용자가 많습니다. http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c -app-crashes – gunjan

0

는 strcat와 같은 간단한 기능을 사용해보십시오() 및 write().

+0

strcat(), strcpy()가 작동합니다. 하지만 fwrite()가 작동하지 않기 때문에 여전히 파일에 정보를 인쇄하거나 덤프 할 수 없습니다. 또한 콜 스택 정보를 얻을 수없는 backtrace() 호출 작업이 필요합니다. 것은 다른 기능의 동일한 파일에서 응용 프로그램을 충돌 시키면 코드가 작동하지만 큰 프로젝트이므로 다른 파일의 다른 함수에서 충돌이 발생해도 작동하지 않습니다. 여기에 완전히 붙어 : – gunjan

+0

쓰기 (1, 버퍼, 크기) (args의 순서에 대해 확실하지 않음)을 사용합니다 .1은 stdout, 2는 stderr입니다. – Arkadiy

3

불행히도 SIGSEGV 처리기에서 많은 것을 안정적으로 수행 할 수는 없습니다. 이런 식으로 생각하십시오. 프로그램에 심각한 오류가 있으며 상태 (힙과 같은 시스템 수준 상태 포함)가 일치하지 않는 상태입니다.

그런 경우 OS가 마술처럼 신호 처리기에서 임의의 코드를 실행할 수 있도록 필요한 힙 및 기타 내부를 수정하지 못하게 할 수 있습니다.

SEGV가 자체 코드에서 발생하면 좋은 해결책은 코어를 사용하고 근본 문제를 해결하는 것입니다. 코어가 공유 라이브러리를 통해 다른 코드에서 발생하는 경우 해당 코드를 완전히 다른 바이너리로 분리하고 두 바이너리간에 통신하는 것이 좋습니다. 그런 다음 라이브러리가 손상되면 주 프로그램이 작동하지 않습니다.

+0

실제로는 20 개가 넘는 큰 프로젝트의 모든 부분입니다. -30 라이브러리를 사용하고 callstack 로그를 덤프 할 수있는 핸들러를 작성해야합니다. 따라서 라이브러리가 힙 내부를 다른 곳 (핸들러가있는 곳)과 통신하지 않을 수도 있다는 인수가 올바른 것일 수 있습니다. 나는 왜 하나의 케이스 (동일한 라이브러리 호출에서)와 다른 모듈 (클래스에서 호출)에서 콜 스택을 인쇄했는지 궁금했다. – gunjan

0

valgrind를 사용할 수없는 이유가 있습니까?

+0

Valgrind를 사용 해 본 적이 한번도 없었습니다. 실제로 디버깅보다는 테스트를 생성해야합니다. 처리기가 충돌시 콜 스택의 로그를 덤프 할 것입니다. 그런 식으로 Valgrind가 도움이되는지 확인해보십시오. – gunjan

0

응용 프로그램이 충돌하면 Linux는 응용 프로그램이 손상되었을 때 응용 프로그램의 상태와 함께 코어 덤프를 만듭니다. 핵심 파일은 gdb를 사용하여 검사 할 수 있습니다. 어떤 코어 파일이 동일한 쉘에서 프로그램이 시작되기 전에

ulimit -c unlimited 

와 코어 파일 크기를 변경 시도를 생성하지 않으면

. 코어 파일의 이름은 보통 core.PID입니다. 여기서 PID는 프로그램의 PID입니다. 코어 파일은 대개/tmp 또는 프로그램이 시작된 디렉토리의 어딘가에 위치합니다.

코어 파일에 대한 더 많은 정보는 코어 용 맨 페이지에서 볼 수 있습니다. 설명서 페이지를 읽기 위해서는

man core 

을 사용하십시오.

+0

감사합니다. – gunjan

0

나는 그것을 부분적으로 작동시킬 수 있었다. 사실 'sudo'모드에서 응용 프로그램을 실행하고있었습니다. 사용자 모드에서 실행하면 콜 스택이 생깁니다. 그러나 사용자 모드에서 실행하면 하드웨어 가속이 비활성화됩니다 (nvidia 그래픽 드라이버). 이를 해결하기 위해 자신을 '비디오'그룹에 추가 했으므로/dev/nvidia0 &/dev/nvidiactl에 액세스 할 수 있습니다. 그러나 액세스 할 때 스택이 더 이상 생성되지 않습니다. 그 때만 나는 사용자 모드이고 하드웨어 가속이 비활성화되어 스택이오고 있습니다. 하지만 하드웨어 가속을 사용하지 않고 애플리케이션을 실행할 수는 없습니다. 중요한 기능을 사용할 수 없게 될 수도 있습니다. 아무도 모른다면 알려주세요.

감사합니다.

관련 문제