gdb -k가 오프라인 디버거로서 동작할 때에는 상당히 높은 수준의 유저 인터페이스를 제공하지만, 몇가지 할 수 없는 작업들이 있습니다. 이 중 제일 중요한 것은 중단점(breakpoint) 걸기와 커널 코드의 단계별 수행(single-step) 기능입니다.
만약 저수준의 커널 디버깅이 필요할 경우, DDB라 불리우는 온라인 디버거를 사용할 수 있습니다. DDB는 중단점 걸기, 커널 함수들의 단계별 수행, 그리고 커널 변수들의 검사 및 변경 등을 할 수 있습니다. 그러나 커널 소스 파일들을 억세스 할 수 없으며, gdb에서와 같은 전체 디버깅 정보가 아닌 전역 및 정적 변수만 억세스 할 수 있습니다.
커널에 DDB를 포함하도록 설정하기 위해서는 설정 파일에 다음의 옵션 줄을 추가하고, 다시 빌드합니다.
options DDB(커널 설정의 자세한 부분은 FreeBSD 커널 설정하기를 참고하시기 바랍니다.
Note: 만약 부트 블럭이 오래된 버전이라면 디버거 심볼이 모두 로딩되지 않을 것입니다. 부트 블럭을 업데이트 하기 바랍니다. 최근 것은 DDB 심볼을 자동으로 로딩합니다.)
DDB 커널 동작 중에 DDB로 들어가는 몇가지 방법들이 있습니다. 첫번째이자 가장 초기의 방법은 부트 프롬프트에서 -d 부트 플래그를 입력하는 것입니다. 커널은 디버깅 모드로 시작할 것이며 장치 검색 이전에 DDB로 들어갑니다. 따라서 장치 검색/연결 함수들도 디버깅할 수 있습니다.
두번째는 키보드 상에서 핫키를 누르는 방법이며, 보통 Ctrl-Alt-ESC 키가 됩니다. 시스템 콘솔에서는 다른 키로 맵핑될 수 있으므로 확인이 필요합니다. 시리얼 콘솔에서는 DDB로 들어가기 위해 콘솔 라인 상에 시리얼 라인 BREAK의 사용을 가능하게 하는 옵션이 있습니다(커널 설정 파일의 options BREAK_TO_DEBUGGER). 이 옵션은 디폴트로 설정되어 있지 않은데, 케이블을 뽑을 때 불필요한 BREAK 상태를 발생시키는 시리얼 어댑터가 많기 때문입니다.
세번째는 커널이 DDB를 이용하도록 설정하여 패닉이 발생했을 경우 DDB로 분기하는 방법입니다. 이 때문에 사람의 손이 가지 않는 시스템의 커널에 DDB를 설정하는 것은 그리 현명한 것이 아닙니다.
DDB 명령들은 대체로 gdb의 명령과 비슷합니다. 아마 여러분이 첫번째로 필요로 하는 기능은 중단점 설정 기능일 것입니다.
b 함수 이름
b 주소숫자는 디폴트로 16진수를 취하며, 심볼 이름과 구분이 되도록 해주어야 합니다. a-f 문자로 시작하는 16진수는 앞에 0x를 붙여주어야 합니다(숫자로 시작하는 값에 대해서는 선택사항입니다). 함수 이름 + 0x103과 같은 간단한 수식도 가능합니다.
작업이 중지된 커널을 다시 동작시키기 위해서는 간단히 다음을 실행하면 됩니다.
c
스택을 추적하기 위해서는 다음을 실행합니다.
trace
Note: 핫키를 이용해서 DDB로 들어갔을 때 커널이 인터럽트 서비스 루틴을 실행하고 있었다면 스택 추적이 소용 없을 때도 있습니다.
만약 중단점을 삭제하고 싶을 때에는 다음을 실행합니다.
del
del 주소식(address-expression)첫번째 형식은 중단점을 만난 상태에서 실행할 수 있으며, 해당 중단점을 삭제합니다. 두번째 형식은 어떤 중단점도 제거할 수 있지만, 명확한 주소를 명시해주어야 합니다. 주소는 다음 명령으로 얻을 수 있습니다.
show b
커널의 단계적인 수행을 위해서 다음을 실행시켜 보시기 바랍니다.
s
이 명령은 함수 안으로 들어가게 됩니다. 그러나 return 문을 만날 때까지 실행하기를 바랄 경우에는 다음 명령을 사용합니다.
n
Note: 이 명령은 gdb의 next문과 다릅니다. gdb의 finish문과 유사합니다.
메모리로부터 데이터를 읽을 때에는 다음을 실행합니다(예제).
x/wx 0xf0133fe0,40
x/hd db_symtab_space
x/bc termbuf,10
x/s stringbuf
각각 워드/하프워드/바이트 억세스 및 16진수/10진수/문자/ 문자열 출력을
보여줍니다. 쉼표 다음의 숫자는 표시할 개체의 수입니다. 다음 0x10 개의
아이템을 출력할 때에는 간단히 다음과 같이 씁니다.
x ,10
이와 동일하게, foofunc 함수의 최초 0x10 개의 명령을 디스어셈블하여 foofunc부터 시작하는 옵셋값과 함께 출력하기 위해서는 다음과 같이 실행합니다.
x/ia foofunc,10
메모리의 내용을 수정하기 위해서, 다음의 쓰기 명령을 사용합니다.
w/b termbuf 0xa 0xb 0
w/w 0xf0010030 0 0명령 다음의 접미사 (b/h/w)들은 쓸 데이터의 크기를 명시하며, 그 다음에 이어지는 첫 수식은 쓸 주소가 되고 나머지는 연속적인 메모리 위치에 쓸 데이터로 해석됩니다.
만약 현재 레지스터의 내용을 알고 싶다면 다음을 실행합니다.
다른 방법으로, 다음과 같이 각 레지스터의 값을 볼 수 있으며,
p $eax다음과 같이 수정할 수 있습니다.
set $eax new-value
DDB에서 커널 함수를 호출할 필요가 있을 경우에는 간단히 다음과 같이 실행하면 됩니다.
call func(arg1, arg2, ...)
실행 후에는 반환값이 출력될 것입니다.
현재 실행 중인 프로세스들에 대해 ps(1) 스타일의 요약을 보고 싶을 경우에는 다음을 실행합니다.
ps
커널에 오류가 발생한 원인을 찾는 과정 다음에, 여러분은 리부팅을 원할 것입니다. 커널 오류의 심각성에 따라 커널의 일부분이 제대로 동작하지 않을 수 있음을 명심하시기 바랍니다. 시스템을 종료하고 다시 리부팅하기 위해서 다음에 열거한 방법들 중 하나를 실행하십시오.
panic
이 명령은 나중에 gdb를 이용하여 고수준에서 코어를 분석할 수 있도록 코어를 덤프하고 리부팅 하도록 합니다. 보통 이 명령은 continue문 다음에 실행되도록 해야 합니다.
call boot(0)
이 명령은 현재 동작 중인 시스템을 깨끗하게 종료시키는 좋은 방법이며, 모든 디스크들을 sync()시키고, 마지막으로 리부팅 합니다. 커널의 디스크와 파일 시스템 인터페이스가 손상되지 않은 한, 제일 깔끔한 종료 방법입니다.
call cpu_reset()
비상 시에 `적색 상황 버튼'을 누를 때와 같은 심각한 문제에 부딪혔을 때 사용하는 최후의 방법입니다.
명령들의 짧은 요약을 보고 싶을 때에는, 다음을 실행합니다.
help
그러나 디버깅 세션에 대한 준비물로 ddb(4) 매뉴얼 페이지의 프린트물을 적극 추천합니다. 커널을 단계적으로 실행할 때 온라인으로 매뉴얼을 읽는 것이 매우 힘이 듦을 명심하시기 바랍니다.