QT/Embedded C++ Exception을 활용한 런타임 콜스택 정보 얻기
페이지 정보
작성자
본문
이글은 제가 podovat.com의 blog 올린글(http://www.podovat.com/?p=432)에서 퍼온것입니다.
[b]1. 개요[/b]
동작중인 프로그램이 갑자기 오류를 발생하여 프로그램이 종료하는 문제를 찾기 위해서는 종료시점의 Call Stack을 확인하여 소스코드 상의 어디까지 수행을 하다 멈추었는지를 찾아서 디버깅을 해야 한다. 이를 위해서 Linux API중 backtrace함수는 호출 시점에 수행한 기록들을 알아낼 수 있는데, 이 함수를 프로그램 오류시 Exception이 발생될 때 호출하여, 그 기록을 파일로 저장하고, 이를 토대로 addr2line 유틸리티를 이용해서 실제 소스코드상의 위치를 알아낼 수 있다.
[b]2. 구현[/b]
1과 같이 설명한 내용을 구현한 내용은 다음과 같으며 excpthandler.h 파일 이름으로 저장을 한다.
[code=c]
#ifndef _EXCPTHANDLER_H_
#define _ECCPTHANDLER_H_
#include
#include
#include
#include
#include
using namespace std;
class ExceptionTracer
{
public:
ExceptionTracer()
{
void * array[100];
int nSize = backtrace(array, 100);
char ** symbols = backtrace_symbols(array, nSize);
// error file
ofstream outFile;
outFile.open("/program-error.log", ios::trunc);
outFile << __DATE__ << " " << __TIME__ << endl;
for (int i = 0; i < nSize; i++)
{
cout << symbols[i] << endl;
outFile << symbols[i] << endl;
}
free(symbols);
}
};
template class SignalTranslator
{
private:
class SingleTonTranslator
{
public:
SingleTonTranslator()
{
signal(SignalExceptionClass::GetSignalNumber(), SignalHandler);
}
static void SignalHandler(int)
{
throw SignalExceptionClass();
}
};
public:
SignalTranslator()
{
static SingleTonTranslator s_objTranslator;
}
};
// An example for SIGSEGV
class SegmentationFault : public ExceptionTracer, public exception
{
public:
static int GetSignalNumber() {return SIGSEGV;}
};
SignalTranslator g_objSegmentationFaultTranslator;
// An example for SIGFPE
class FloatingPointException : public ExceptionTracer, public exception
{
public:
static int GetSignalNumber() {return SIGFPE;}
};
SignalTranslator g_objFloatingPointExceptionTranslator;
// An example for SIGILL
class IllegalInstructionException : public ExceptionTracer, public exception
{
public:
static int GetSignalNumber() {return SIGILL;}
};
SignalTranslator
g_objIllegalInstructionExceptionTranslator;
// An example for SIGTRAP
class TrapException : public ExceptionTracer, public exception
{
public:
static int GetSignalNumber() {return SIGTRAP;}
};
SignalTranslator g_objTrapExceptionTranslator;
class ExceptionHandler
{
private:
class SingleTonHandler
{
public:
SingleTonHandler()
{
set_terminate(Handler);
}
static void Handler()
{
// Exception from construction/destruction of global variables
try
{
// re-throw
throw;
}
catch (SegmentationFault &)
{
cout << "SegmentationFault" << endl;
}
catch (FloatingPointException &)
{
cout << "FloatingPointException" << endl;
}
catch (IllegalInstructionException &)
{
cout << "IllegalInstructionException" << endl;
}
catch (TrapException &)
{
cout << "TrapException" << endl;
}
catch (...)
{
cout << "Unknown Exception" << endl;
}
//if this is a thread performing some core activity
abort();
// else if this is a thread used to service requests
// pthread_exit();
}
};
public:
ExceptionHandler()
{
static SingleTonHandler s_objHandler;
}
};
// Before defining any global variable, we define a dummy instance
// of ExceptionHandler object to make sure that
// ExceptionHandler::SingleTonHandler::SingleTonHandler() is invoked
ExceptionHandler g_objExceptionHandler;
#endif
[/code]
위의 구현 파일을 excpthandler.h 파일로 저장한다.
[b]3. 예제[/b]
다음과 같은 예제를 만들어서 excpthandler.h 파일을 include 한다.
main.cpp
[code=c]
#include
#include
#include "excpthandler.h"
int main(int argc, char** argv)
{
QString* s1 = new QString("111");
QString* s2 = new QString("222");
QString* string[2] = { s1, s2 };
for (int i=0; i<3; i++)
{
qDebug() << "string:" << *string[i];
}
return 0;
}
[/code]
위의 코드를 컴파일 하기 위해서 다음과 같이 Project파일을 생성한다.
excpthandler.pro
[code]
TEMPLATE = app
QT += core
QT -= gui
CONFIG += debug
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
# Input
SOURCES += main.cpp
[/code]
command상에서 다음과 같이 compile을 한다.
$ qmake
$ make
[b]4. 실행 및 디버깅[/b]
다음과 같이 프로그램을 실행하면 에러가 발생한다.
[code]
$ ./excpthandler
podo@ubuntu:~/excpthandler$ ./excpthandler
string: "111"
string: "222"
./excpthandler() [0x80497c4]
./excpthandler() [0x8049cff]
./excpthandler() [0x8049d8c]
[0xb7784400]
/usr/lib/i386-linux-gnu/libQtCore.so.4(_ZN11QTextStreamlsERK7QString+0x3a) [0xb75ae8aa]
./excpthandler() [0x8049752]
./excpthandler() [0x8049262]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xb71ff4d3]
./excpthandler() [0x8049131]
SegmentationFault
Aborted (core dumped)
[/code]
위의 표시 내용중 Segmentation Fault발생 이전까지 주소들이 표시가 되는데, 이 주소값을 이용해 소스코드 상의 어디인지 확인할 수 있다.
이를 위해 addr2line 프로그램을 이용해서 주소값을 입력하여 소스코드상의 위치를 확인한다.
[code]
podo@ubuntu:~/excpthandler$ addr2line -C -fe ./excpthandler 0x80497c4
ExceptionTracer
/home/podo/excpthandler/excpthandler.h:19
podo@ubuntu:~/excpthandler$ addr2line -C -fe ./excpthandler 0x8049cff
SegmentationFault
/home/podo/excpthandler/excpthandler.h:61
podo@ubuntu:~/excpthandler$ addr2line -C -fe ./excpthandler 0x8049d8c
SignalTranslator::SingleTonTranslator::SignalHandler(int)
/home/podo/excpthandler/excpthandler.h:49
podo@ubuntu:~/excpthandler$ addr2line -C -fe ./excpthandler 0x8049752
QDebug::operator<<(QString const&)
/usr/include/qt4/QtCore/qdebug.h:112
podo@ubuntu:~/excpthandler$ addr2line -C -fe ./excpthandler 0x8049262
main
/home/podo/excpthandler/main.cpp:14
[/code]
addr2line 을 이용해서 주소값을 차례대로 입력하다보면 main.cpp 의 14번째 라인에서 이상이 있음을 확인할 수 있다.
[b]1. 개요[/b]
동작중인 프로그램이 갑자기 오류를 발생하여 프로그램이 종료하는 문제를 찾기 위해서는 종료시점의 Call Stack을 확인하여 소스코드 상의 어디까지 수행을 하다 멈추었는지를 찾아서 디버깅을 해야 한다. 이를 위해서 Linux API중 backtrace함수는 호출 시점에 수행한 기록들을 알아낼 수 있는데, 이 함수를 프로그램 오류시 Exception이 발생될 때 호출하여, 그 기록을 파일로 저장하고, 이를 토대로 addr2line 유틸리티를 이용해서 실제 소스코드상의 위치를 알아낼 수 있다.
[b]2. 구현[/b]
1과 같이 설명한 내용을 구현한 내용은 다음과 같으며 excpthandler.h 파일 이름으로 저장을 한다.
[code=c]
#ifndef _EXCPTHANDLER_H_
#define _ECCPTHANDLER_H_
#include
#include
#include
#include
#include
using namespace std;
class ExceptionTracer
{
public:
ExceptionTracer()
{
void * array[100];
int nSize = backtrace(array, 100);
char ** symbols = backtrace_symbols(array, nSize);
// error file
ofstream outFile;
outFile.open("/program-error.log", ios::trunc);
outFile << __DATE__ << " " << __TIME__ << endl;
for (int i = 0; i < nSize; i++)
{
cout << symbols[i] << endl;
outFile << symbols[i] << endl;
}
free(symbols);
}
};
template class SignalTranslator
{
private:
class SingleTonTranslator
{
public:
SingleTonTranslator()
{
signal(SignalExceptionClass::GetSignalNumber(), SignalHandler);
}
static void SignalHandler(int)
{
throw SignalExceptionClass();
}
};
public:
SignalTranslator()
{
static SingleTonTranslator s_objTranslator;
}
};
// An example for SIGSEGV
class SegmentationFault : public ExceptionTracer, public exception
{
public:
static int GetSignalNumber() {return SIGSEGV;}
};
SignalTranslator g_objSegmentationFaultTranslator;
// An example for SIGFPE
class FloatingPointException : public ExceptionTracer, public exception
{
public:
static int GetSignalNumber() {return SIGFPE;}
};
SignalTranslator g_objFloatingPointExceptionTranslator;
// An example for SIGILL
class IllegalInstructionException : public ExceptionTracer, public exception
{
public:
static int GetSignalNumber() {return SIGILL;}
};
SignalTranslator
g_objIllegalInstructionExceptionTranslator;
// An example for SIGTRAP
class TrapException : public ExceptionTracer, public exception
{
public:
static int GetSignalNumber() {return SIGTRAP;}
};
SignalTranslator g_objTrapExceptionTranslator;
class ExceptionHandler
{
private:
class SingleTonHandler
{
public:
SingleTonHandler()
{
set_terminate(Handler);
}
static void Handler()
{
// Exception from construction/destruction of global variables
try
{
// re-throw
throw;
}
catch (SegmentationFault &)
{
cout << "SegmentationFault" << endl;
}
catch (FloatingPointException &)
{
cout << "FloatingPointException" << endl;
}
catch (IllegalInstructionException &)
{
cout << "IllegalInstructionException" << endl;
}
catch (TrapException &)
{
cout << "TrapException" << endl;
}
catch (...)
{
cout << "Unknown Exception" << endl;
}
//if this is a thread performing some core activity
abort();
// else if this is a thread used to service requests
// pthread_exit();
}
};
public:
ExceptionHandler()
{
static SingleTonHandler s_objHandler;
}
};
// Before defining any global variable, we define a dummy instance
// of ExceptionHandler object to make sure that
// ExceptionHandler::SingleTonHandler::SingleTonHandler() is invoked
ExceptionHandler g_objExceptionHandler;
#endif
[/code]
위의 구현 파일을 excpthandler.h 파일로 저장한다.
[b]3. 예제[/b]
다음과 같은 예제를 만들어서 excpthandler.h 파일을 include 한다.
main.cpp
[code=c]
#include
#include
#include "excpthandler.h"
int main(int argc, char** argv)
{
QString* s1 = new QString("111");
QString* s2 = new QString("222");
QString* string[2] = { s1, s2 };
for (int i=0; i<3; i++)
{
qDebug() << "string:" << *string[i];
}
return 0;
}
[/code]
위의 코드를 컴파일 하기 위해서 다음과 같이 Project파일을 생성한다.
excpthandler.pro
[code]
TEMPLATE = app
QT += core
QT -= gui
CONFIG += debug
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
# Input
SOURCES += main.cpp
[/code]
command상에서 다음과 같이 compile을 한다.
$ qmake
$ make
[b]4. 실행 및 디버깅[/b]
다음과 같이 프로그램을 실행하면 에러가 발생한다.
[code]
$ ./excpthandler
podo@ubuntu:~/excpthandler$ ./excpthandler
string: "111"
string: "222"
./excpthandler() [0x80497c4]
./excpthandler() [0x8049cff]
./excpthandler() [0x8049d8c]
[0xb7784400]
/usr/lib/i386-linux-gnu/libQtCore.so.4(_ZN11QTextStreamlsERK7QString+0x3a) [0xb75ae8aa]
./excpthandler() [0x8049752]
./excpthandler() [0x8049262]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xb71ff4d3]
./excpthandler() [0x8049131]
SegmentationFault
Aborted (core dumped)
[/code]
위의 표시 내용중 Segmentation Fault발생 이전까지 주소들이 표시가 되는데, 이 주소값을 이용해 소스코드 상의 어디인지 확인할 수 있다.
이를 위해 addr2line 프로그램을 이용해서 주소값을 입력하여 소스코드상의 위치를 확인한다.
[code]
podo@ubuntu:~/excpthandler$ addr2line -C -fe ./excpthandler 0x80497c4
ExceptionTracer
/home/podo/excpthandler/excpthandler.h:19
podo@ubuntu:~/excpthandler$ addr2line -C -fe ./excpthandler 0x8049cff
SegmentationFault
/home/podo/excpthandler/excpthandler.h:61
podo@ubuntu:~/excpthandler$ addr2line -C -fe ./excpthandler 0x8049d8c
SignalTranslator::SingleTonTranslator::SignalHandler(int)
/home/podo/excpthandler/excpthandler.h:49
podo@ubuntu:~/excpthandler$ addr2line -C -fe ./excpthandler 0x8049752
QDebug::operator<<(QString const&)
/usr/include/qt4/QtCore/qdebug.h:112
podo@ubuntu:~/excpthandler$ addr2line -C -fe ./excpthandler 0x8049262
main
/home/podo/excpthandler/main.cpp:14
[/code]
addr2line 을 이용해서 주소값을 차례대로 입력하다보면 main.cpp 의 14번째 라인에서 이상이 있음을 확인할 수 있다.
추천0
관련링크
- http://www.podovat.com/?p=432 2084회 연결
댓글목록
인라이너님의 댓글

구글의 breakpad 가 아마 비슷한 기능을 했던거 같네요.