디버깅 기법
여기에서는 Qt 기반 소프트웨어 디버깅에 도움이 되는 몇 가지 유용한 힌트를 소개합니다.
디버깅을 위한 Qt 구성하기
설치를 위해 Qt를 구성할 때 응용 프로그램과 라이브러리의 버그를 쉽게 추적할 수 있도록 디버그 심볼을 포함하도록 빌드할 수 있습니다. 그러나 일부 플랫폼에서는 디버그 모드에서 Qt를 빌드하면 애플리케이션이 바람직하지 않게 커질 수 있습니다.
macOS 및 Xcode에서 디버깅하기
프레임워크 포함/미포함 디버깅하기
디버그 라이브러리 및 프레임워크에 대해 알아야 할 기본적인 내용은 developer.apple.com에서 확인할 수 있습니다: Apple 기술 노트 TN2124.
Qt를 빌드할 때 프레임워크는 기본적으로 빌드되며, 프레임워크 내부에는 릴리스 버전과 디버그 버전(예: QtCore 및 QtCore_debug)이 모두 있습니다. Qt를 빌드할 때 -no-framework
플래그를 전달하면 각 Qt 라이브러리에 대해 두 개의 dylib가 빌드됩니다(예: libQtCore.4.dylib 및 libQtCore_debug.4.dylib).
링크할 때 어떤 일이 발생하는지는 프레임워크 사용 여부에 따라 다릅니다. 어느 쪽을 다른 쪽보다 더 권장할 만한 이유는 없습니다.
프레임워크 사용:
릴리스 및 디버그 라이브러리가 프레임워크 내부에 있으므로 앱은 프레임워크에 대해 간단히 링크됩니다. 그런 다음 디버거에서 실행하면 DYLD_IMAGE_SUFFIX
설정 여부에 따라 릴리스 버전 또는 디버그 버전 중 하나를 얻게 됩니다. 설정하지 않으면 기본적으로 릴리스 버전이 표시됩니다(즉, _debug가 아닌 버전). DYLD_IMAGE_SUFFIX=_debug
을 설정하면 디버그 버전을 받습니다.
프레임워크 없이:
디버그 구성이 포함된 메이크파일을 생성하도록 qmake에 지시하면 _debug 버전의 라이브러리에 대해 링크하고 앱에 대한 디버그 심볼을 생성합니다. 그러면 GDB에서 이 프로그램을 실행하면 다른 플랫폼에서 GDB를 실행하는 것처럼 작동하며 Qt 내부를 추적할 수 있습니다.
Qt에서 인식하는 명령줄 옵션
Qt 애플리케이션을 실행할 때 디버깅에 도움이 되는 몇 가지 명령줄 옵션을 지정할 수 있습니다. 이러한 옵션은 QApplication 에서 인식합니다.
옵션 | 설명 |
---|---|
-nograb | 응용 프로그램은 the mouse 또는 the keyboard 을 가져와서는 안 됩니다. 이 옵션은 프로그램이 Linux의 gdb 디버거에서 실행될 때 기본적으로 설정됩니다. |
-dograb | 암시적이든 명시적이든 -nograb 을 무시합니다. -dograb 은 명령줄의 마지막에 -nograb 이 있어도 -nograb 보다 우선합니다. |
Qt가 인식하는 환경 변수
런타임에 Qt 애플리케이션은 많은 환경 변수를 인식하며, 그 중 일부는 디버깅에 유용할 수 있습니다:
변수 | 설명 |
---|---|
QT_DEBUG_PLUGINS | 0이 아닌 값으로 설정하면 Qt가 로드하려는 각 (C++) 플러그인에 대한 진단 정보를 출력합니다. |
QML_IMPORT_TRACE | 0이 아닌 값으로 설정하면 QML이 임포트 로딩 메커니즘에서 진단 정보를 출력합니다. |
QT_HASH_SEED | 정수로 설정하면 각 애플리케이션 실행에 대해 새로운 임의 순서를 사용하여 QHash 및 QSet 을 비활성화하여 경우에 따라 테스트 및 디버깅을 어렵게 만들 수 있습니다. |
QT_WIN_DEBUG_CONSOLE | Windows에서는 GUI 애플리케이션이 콘솔에 연결되지 않으므로 stdout 및 stderr 에 기록된 출력은 사용자에게 표시되지 않습니다. IDE는 일반적으로 출력을 리디렉션하여 표시하지만 명령줄에서 애플리케이션을 실행하면 디버그 출력이 손실됩니다. 출력에 액세스하려면 이 환경 변수를 new 으로 설정하여 애플리케이션이 새 콘솔을 할당하도록 하거나 attach 으로 설정하여 애플리케이션이 부모 프로세스의 콘솔에 연결을 시도하도록 하세요. |
경고 및 디버깅 메시지
Qt에는 경고 및 디버그 텍스트를 작성하기 위한 전역 C++ 매크로가 포함되어 있습니다. 일반 매크로는 기본값 logging category 을 사용하며, 범주화된 로깅 매크로를 사용하면 범주를 지정할 수 있습니다. 다음과 같은 용도로 사용할 수 있습니다:
일반 매크로 | 카테고리 매크로 | 목적 |
---|---|---|
qDebug() | qCDebug() | 사용자 정의 디버그 출력 작성에 사용됩니다. |
qInfo() | qCInfo() | 정보 메시지에 사용 |
qWarning() | qCWarning() | 애플리케이션 또는 라이브러리에서 경고 및 복구 가능한 오류를 보고하는 데 사용됩니다. |
qCritical() | qCCritical() | 치명적인 오류 메시지를 작성하고 시스템 오류를 보고하는 데 사용됩니다. |
qFatal() | - | 종료 직전에 치명적인 오류에 대한 메시지를 작성하는 데 사용됩니다. |
<QtDebug> 헤더 파일을 포함하면 qDebug()
매크로를 출력 스트림으로 사용할 수도 있습니다. 예를 들어
qDebug() << "Widget" << widget << "at position" << widget->pos();
이 매크로의 Qt 구현은 Unix/X11과 macOS에서 stderr
출력에 출력됩니다. Windows의 경우 콘솔 애플리케이션인 경우 텍스트가 콘솔로 전송되고, 그렇지 않은 경우 디버거로 전송됩니다.
기본적으로 메시지만 인쇄됩니다. QT_MESSAGE_PATTERN
환경 변수를 설정하여 추가 정보를 포함할 수 있습니다. 예를 들어
QT_MESSAGE_PATTERN="[%{time process} %{type}] %{appname} %{category} %{function} - %{message}"
형식은 qSetMessagePattern()에 문서화되어 있습니다. qInstallMessageHandler ()를 사용하여 자체 메시지 핸들러를 설치할 수도 있습니다.
QT_FATAL_WARNINGS
환경 변수가 설정되어 있으면 qWarning()는 경고 메시지를 인쇄한 후 종료됩니다. 이렇게 하면 디버거에서 백트레이스를 쉽게 얻을 수 있습니다.
qDebug(), qInfo(), qWarning()은 디버깅 도구입니다. 컴파일 중에 QT_NO_DEBUG_OUTPUT
, QT_NO_INFO_OUTPUT
, 또는 QT_NO_WARNING_OUTPUT
를 정의하여 컴파일할 수 있습니다.
디버깅 함수 QObject::dumpObjectTree() 및 QObject::dumpObjectInfo()은 애플리케이션이 이상하게 보이거나 동작할 때 유용합니다. object names 을 사용하는 것이 그렇지 않은 경우보다 더 유용하지만, 이름 없이도 유용할 때가 많습니다.
QML에서는 dumpItemTree()도 같은 용도로 사용됩니다.
qDebug() 스트림 연산자 지원 제공
qDebug()에서 사용하는 스트림 연산자를 구현하여 클래스에 대한 디버깅 지원을 제공할 수 있습니다. 스트림을 구현하는 클래스는 QDebug
입니다. QDebugStateSaver
을 사용하여 스트림의 서식 옵션을 임시로 저장합니다. nospace () 및 QTextStream manipulators 을 사용하여 서식을 추가로 사용자 정의합니다.
다음은 2D 좌표를 나타내는 클래스의 예제입니다.
QDebug operator<<(QDebug dbg, const Coordinate &c) { QDebugStateSaver saver(dbg); dbg.nospace() << "(" << c.x() << ", " << c.y() << ")"; return dbg; }
사용자 정의 유형과 Qt의 메타 객체 시스템의 통합은 사용자 정의 Qt 유형 만들기 문서에서 더 자세히 다룹니다.
매크로 디버깅
헤더 파일 <QtGlobal>
에는 몇 가지 디버깅 매크로와 #define
s가 포함되어 있습니다.
세 가지 중요한 매크로가 있습니다:
- Q_ASSERT(cond), 여기서
cond
는 부울 표현식이며, "ASSERT:'cond' 파일 xyz.cpp, 234줄"이라는 경고를 쓰고cond
가 거짓이면 종료합니다. - Q_ASSERT_X
cond
는 부울 표현식, 은 위치, 은 메시지인 (cond, where, what)은 경고를 출력합니다: " : ' ', 파일 xyz.cpp, 234줄에서 ASSERT 실패", 가 거짓이면 종료합니다.where
what
where
what
cond
- Q_CHECK_PTR(ptr), 여기서
ptr
은 포인터입니다. "파일 xyz.cpp, 234줄에서: 메모리가 부족합니다."라는 경고를 쓰고ptr
가 0이면 종료합니다.
이러한 매크로는 다음과 같이 프로그램 오류를 감지하는 데 유용합니다:
char *alloc(int size) { Q_ASSERT(size > 0); char *ptr = new char[size]; Q_CHECK_PTR(ptr); return ptr; }
Q_ASSERT(), Q_ASSERT_X(), Q_CHECK_PTR()은 컴파일 중에 QT_NO_DEBUG
이 정의되어 있으면 아무것도 확장되지 않습니다. 따라서 이러한 매크로의 인수는 부작용이 없어야 합니다. 다음은 Q_CHECK_PTR()의 잘못된 사용법입니다:
char *alloc(int size) { char *ptr; Q_CHECK_PTR(ptr = new char[size]); // WRONG return ptr; }
이 코드가 QT_NO_DEBUG
정의된 상태로 컴파일되면 Q_CHECK_PTR() 식의 코드가 실행되지 않고 alloc이 초기화되지 않은 포인터를 반환합니다.
Qt 라이브러리에는 프로그래밍 오류가 감지되면 경고 메시지를 출력하는 수백 개의 내부 검사가 포함되어 있습니다. 따라서 Qt 기반 소프트웨어를 개발할 때는 디버그 버전의 Qt를 사용하는 것이 좋습니다.
QML에서도 로깅 및 categorized logging 을 사용할 수 있습니다.
일반적인 버그
너무 흔해서 여기서 언급할 만한 버그가 하나 있습니다: 클래스 선언에 Q_OBJECT 매크로를 포함하고 메타 객체 컴파일러 (moc
)를 실행했지만 moc
-생성된 객체 코드를 실행 파일에 링크하는 것을 잊어버린 경우 매우 혼란스러운 오류 메시지가 표시됩니다. vtbl
, _vtbl
, __vtbl
또는 이와 유사한 링크 오류는 이 문제로 인한 것일 수 있습니다.
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.