41. DEBUG 출력문 내부 구조와 DEBUG 문 제어를 위한 PCD 분석, 그리고 OVMF 부트 로그 가져오기
이번 장에서는 EDKII 코드 베이스에 존재하는 DEBUG 매크로와 관련된 정보를 학습한다. 다음은 그 예시이다.
DEBUG ((EFI_D_ERROR, "Hello Debug! Check this variable: %d\n", MyVar));일반적인 출력 형식과 유사하지만 다른 기능이 존재한다.
로그 레벨을 이용하여 로그 메세지를 분류할 수 있다. PCD를 통한 구성으로 type별 디버그 메세지를 쉽게 켜고 끌 수 있다.
서로 다른 UEFI 단계 및 모듈은 서로 다른
DEBUG구현을 가지기 때문에 여러DEBUG기능 구현이 존재한다.
사전에 정의된 로그 메세지의 범주는 아래와 같다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/DebugLib.h
#define DEBUG_INIT 0x00000001 // Initialization
#define DEBUG_WARN 0x00000002 // Warnings
#define DEBUG_LOAD 0x00000004 // Load events
#define DEBUG_FS 0x00000008 // EFI File system
#define DEBUG_POOL 0x00000010 // Alloc & Free (pool)
#define DEBUG_PAGE 0x00000020 // Alloc & Free (page)
#define DEBUG_INFO 0x00000040 // Informational debug messages
#define DEBUG_DISPATCH 0x00000080 // PEI/DXE/SMM Dispatchers
#define DEBUG_VARIABLE 0x00000100 // Variable
#define DEBUG_BM 0x00000400 // Boot Manager
#define DEBUG_BLKIO 0x00001000 // BlkIo Driver
#define DEBUG_NET 0x00004000 // Network Io Driver
#define DEBUG_UNDI 0x00010000 // UNDI Driver
#define DEBUG_LOADFILE 0x00020000 // LoadFile
#define DEBUG_EVENT 0x00080000 // Event messages
#define DEBUG_GCD 0x00100000 // Global Coherency Database changes
#define DEBUG_CACHE 0x00200000 // Memory range cachability changes
#define DEBUG_VERBOSE 0x00400000 // Detailed debug messages that may
// significantly impact boot performance
#define DEBUG_ERROR 0x80000000 // Error하지만 일반적으로 다음 별칭의 값이 사용된다.
DEBUG 매크로 자체 또한 DebugLib에 정의되어 있다.
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/DebugLib.h
모든 전처리 과정을 분할하여 살펴보면 일반적으로 다음과 같다.
DEBUG 매크로 인터페이스는 DebugLib 헤더에 정의되어 있지만 실제 구현을 위해서는 사용되는 특정 라이브러리의 인터페이스를 살펴봐야 한다https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkgX64.dsc 를 살펴보자
DebugLib은 각 UEFI 단계에서 각기 다른 구현을 가지고 있다.DebugLib은DEBUG_ON_SERAIL_PORT정의에 따라 다른 구현이 있다.
기본적으로 DEBUG_ON_SERIAL_PORT가 정의되어 있지 않은 경우 PlatformDebugLibIoPort 가 기본 라이브러리이다.
https://github.com/tianocore/edk2/tree/master/OvmfPkg/Library/PlatformDebugLibIoPort
DEBUG 매크로는 다음과 같이 번역된다.
아래를 통해DebugPrintEnabled,DebugPrintLevelEnabled ,DebugPrint 정보를 살펴보자. https://github.com/tianocore/edk2/blob/master/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLib.c
DebugPrintEnabled
DEBUG 매크로는 먼저 DebugPrintEnabled 결과를 확인한다. 이 함수는 PCD의 PcdDebugPropertyMask에 DEBUG_PROPERTY_DEBUG_PRINT_ENABLED가 설정되어 있는지 확인한다.
DebugPrintLevelEnabled
그리고 DebugPrintLevelEnabled 함수는 전달된 ErrorLevel 이 PCD의 PcdFixedDebugPrintErrorLevel 에 있는지 확인한다.
DebugPrint
그런 다음 DebugPrint 함수가 실행되며 단순히 제어를 DebugVPrint로 전달한다.
그리고 DebugVPrint 는 DebugPrintMarker로 제어를 전달한다.
DebugPrintMarker 는 주요 디버그 기능으로, 전달된 ErrorLevel 이 GetDebugPrintErrorLevel() 출력에 있는지 확인한다. 최종적으로는 PCD의 PcdDebugIoPort 에 의해 정의된 입출력 포트에 디버그 문자열 쓰기를 수행한다.
GetDebugPrintErrorLevel() 는 DebugPrintErrorLevelLib 라이브러리에 정의된 함수로 OvmfPkgX64.dsc 에서도 그 구현을 확인할 수 있다.
소스를 보면 단순히 다른 PCD를 확인하는 것을 볼 수 있다. https://github.com/tianocore/edk2/blob/master/MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.c
요약
DEBUG (( ErrorLevel, String, ... )) 와 같은 형태의 코드를 정리하자면 다음과 같다.
PcdDebugPropertyMask에DEBUG_PROPERTY_DEBUG_PRINT_ENABLED가 있는지 확인한다.전달된
ErrorLevel이PcdFixedDebugPrintErrorLevel에 설정되어 있는지 확인한다.전달된
ErrorLevel이PcdDebugPrintErrorLevel에 설정되어 있는지 확인한다.지정된 형식 문자열을
PcdDebugIoPort에 정의된 입출력 포트에 작성한다.
Check if PcdDebugPropertyMask has DEBUG_PROPERTY_DEBUG_PRINT_ENABLED
PcdDebugPropertyMask has DEBUG_PROPERTY_DEBUG_PRINT_ENABLEDOVMF의 DSC 파일에서는 PCD PcdDebugPropertyMask 를 다음과 같이 정의한다.
https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkgX64.dsc
그리고 이 PCD는 Shell용으로 재정의 된다.
이 PCD에 대한 비트 정의는 다음에서 확인할 수 있다. https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dec
SOURCE_DEBUG_ENABLE 의 설정 값
TRUE(17h): Debug Assert(1h)/Debug Print(2h)/Debug Code(4h)/BreakPoint(10h) 활성화
FALSE(2Fh): Debug Assert(1h)/Debug Print(2h)/Debug Code(4h)/Clear Memory(8h)/DeadLoop(20h) 활성화
DEBUG 매크로에서 이 PCD 를 DEBUG_PROPERTY_DEBUG_PRINT_ENABLED 와 비교해보자.
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/DebugLib.h
보다시피 해당 검사는 SOURCE_DEBUG_ENABLE 와 관계없이 통과된다.
Checks if passed ErrorLevel is set in PcdFixedDebugPrintErrorLevel
ErrorLevel is set in PcdFixedDebugPrintErrorLevel해당 PCD는 MdePkg.dsc 에 정의되었다.
https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dec
이 PCD 정보는 OVMF의 DSC 파일에서 재정의 되지 않기 때문에 빌드에 포함되는 값이다. 0xFFFFFFFF 는 디버그 메세지가 EFI_D_* 출력 레벨에 관계없이 이 검사를 통과함을 의미한다.
Checks if passed ErrorLevel is set in PcdDebugPrintErrorLevel
ErrorLevel is set in PcdDebugPrintErrorLevel해당 PCD는 MdePkg.dec 에 정의되었다.
https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dec
기본적으로 MdePkg.dec 및 MdePkg.dsc 에서 0x80000000 으로 정의된다.
https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dsc
0x80000000 이라는 값은 오류 메시지(EFI_D_ERROR) 만 출력함을 의미한다.
빌드하는 OVMF의 DSC 파일에서는 해당 PCD를 재정의하고 있다.
0x8000004F - EFI_D_ERROR | EFI_D_INFO | EFI_D_FS | EFI_D_LOAD | EFI_D_WARN | EFI_D_INIT
만약 DEBUG 가 활성화된다면 위 범주의 메세지가 출력된다.
Writes formatted String to I/O port defined by PcdDebugIoPort
PcdDebugIoPortPcdDebugIoPort 에 대한 정의는 OvmfPkg.dec 에 존재한다.
https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkg.dec
해당 PCD는 PlatformDebugLibIoPort 의 디버그 메세지에 대한 대상 입출력 포트를 제어한다.
DEBUG 테스트
디버그 메세지를 보기 위해서는 디버그 모드에서 OVMF를 다시 컴파일 해야 한다.
먼저 다음과 같이 DEBUG 모드로 빌드를 진행한다.
결과에 대한 경로는Build/OvmfX64/DEBUG_GCC5 이다.
QEMU를 다음과 같은 옵션으로 실행한다.
이렇게 하면 OVMF의 모든 DEBUG 로그 메세지가 debug.log 파일에 포함되어 생성된다. 해당 파일은 매 실행마다 생성된다.
로그파일에서 GUID 치환하기
debug.log 파일을 확인해보면 다음과 같은 GUID 값을 확인할 수 있다.
edk2 빌드 시스템이 GUID의 이름을 가진 파일을 만들기 때문에 이를 이용하면 GUID 값에 대응하는 텍스트로 대체시켜 가독성을 높일 수 있다.
다음과 같은 파이썬 코드를 작성하자.
결과물은 debug_parsed.log 에 저장되며 추가적인GUID 값이 존재하는 경우 EXTRA_GUIDS_FILE_PATH 경로에 지정하여 추가할 수 있다.
추가적으로 해당 스크립트를 범용적으로 이용하기 위해서 아래와 같이 작성해볼 수 있다.
DEBUG 메세지 출력길이 제한
DEBUg 문으로 출력할 수 있는 최대 문자열의 길이가 제한되어 있다. https://github.com/tianocore/edk2/blob/master/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLib.c
정의에 따라 0x100보다 긴 출력이 발생할 수 있으므로 해당 값을 변경하여 빌드함으로써 그 제한을 늘릴 수 있다.
\
Last updated