FreeBSD 핸드북 : 커널 디버깅 : DDB를 이용한 온라인 커널 디버깅
Previous: 덤프의 사후 분석(post-mortem)
Next: 원격 GDB를 이용한 온라인 커널 디버깅

22.3. DDB를 이용한 온라인 커널 디버깅

오프라인 디버거로서 kgdb는 매우 높은 수준의 유저 인터페이스를 제공하지만, 할 수 없는 작업이 몇가지 있습니다. 이들 중에서 제일 중요한 것들이 중단점(breakpoint) 걸기와, 커널 코드의 단계적 수행(single-step)입니다.

만약 커널에 대한 저수준의 디버깅이 필요할 경우, DDB라 불리우는 온라인 디버거를 사용할 수 있습니다. DDB는 중단점 걸기, 커널 함수의 단계적 수행, 커널 변수의 검사 및 변경 등의 작업을 할 수 있습니다. 그러나 커널 소스 파일은 억세스 할 수 없으며, kgdb에서와 같은 모든 디버깅 정보가 아닌 전역 및 정적 변수에 대한 억세스만 가능합니다.

커널에 DDB를 포함하도록 설정하기 위해서는, 설정 파일에 다음의 옵션 라인을 추가하고 커널을 다시 빌드하기 바랍니다.

        options DDB
(FreeBSD 커널 설정에 대한 자세한 내용은 커널 설정을 보시기 바랍니다. 만약 부트 블럭이 오래된 버전이라면, 디버거 심볼이 모두 로딩되지 않을 것입니다. 부트 블럭을 업데이트하시기 바랍니다. 최근의 것은 DDB 심볼을 로딩할 것입니다.)

DDB 커널이 실행되었을 때, DDB로 들어가는 방법이 몇가지 있습니다. 첫번째이자 가장 빠른 방법은 부트 프롬프트에서 -d 부트 플래그를 입력는 것입니다. 커널은 디버그 모드로 시작될 것이며, 장치 체크 이전에 먼저 DDB로 들어갈 것입니다. 그러므로 장치 체크/연결 함수들도 디버그할 수 있습니다.

두번째 시나리오는 키보드의 Ctrl-Alt-ESC 핫키를 사용하는 것입니다. 시스템 콘솔에서는 이 핫키가 바뀔 수 있으므로, 확인을 해야 합니다. 콘솔 라인 상에서 시리얼 라인 BREAK를 사용하여 DDB로 들어갈 수 있는 시리얼 콘솔을 위한 옵션이 있습니다(커널 설정 파일에서의 ``options BREAK_TO_DEBUGGER''). 이 옵션은 디폴트로 설정되어 있지 않은데, 그 이유는 케이블을 뽑을 때처럼 불필요하게 BREAK 상태를 발생시키는 필요 없는 시리얼 어댑터가 많이 존재하기 때문입니다.

세번째 방법은 커널이 DDB를 사용하도록 설정했을 경우 패닉 상태가 발생하여 DDB로 실행이 분기되는 것입니다. 따라서 되도록 손을 대지 않으려는 시스템에 DDB 옵션을 설정하는 것은 그다지 현명한 생각이 아닙니다.

DDB 명령들은 대체로 gdb의 몇가지 명령들과 닮았습니다. 여러분이 첫번째로 원하는 기능은 아마 중단점의 설정일 것입니다.

  b 함수 이름
  b 주소

숫자는 디폴트로 16진수로 인식합니다. 그러나 a-f 문자로 시작하는 16진수들을 일반 심볼 이름과 구분하기 위하여 앞에 0x를 붙입니다(다른 숫자들에 대해서는 옵션입니다). 함수 이름 + 0x103 와 같은 간단한 수식은 사용이 가능합니다.

작업이 중단된 커널을 다시 동작시키기 위해서는 간단히

  c
를 치면 됩니다. 스택 추적을 얻기 위해서는
  trace
치면 됩니다. DDB에 핫키를 이용해서 들어갔을 때, 현재 커널은 인터럽트를 수행 중에 있는 상태이기 때문에 스택 추적은 별로 소용이 없을 수 있습니다.

중단점을 삭제하기 위해서는 다음을 수행하면 됩니다.

  del
  del 주소(address-expression)
첫번째 형식은 중단점을 만난 상태에서 바로 사용할 수 있는 것이며, 현재의 중단점을 삭제합니다. 두번째 형식은 어느 곳의 중단점도 지울 수 있지만, 명확한 위치를 지정해야 합니다. 위치는 다음의 명령으로부터 얻을 수 있습니다.
  show b
커널을 단계적으로 수행하기 위해서는 다음의 명령을 수행하기 바랍니다.
  s
이 명령은 함수들 속으로 들어갈 것입니다. 그러나 return 문을 만난 시점에서 DDB 추적을 실행할 때에는 다음의 명령을 사용합니다.
  n
*주* 이 명령은 gdb의 `next' 명령과는 차이가 있습니다. gdb의 `finish'와 같습니다.

메모리로부터 데이터를 확인하기 위해서는 다음을 실행합니다(예제).

  x/wx 0xf0133fe0,40
  x/hd db_symtab_space
  x/bc termbuf,10
  x/s stringbuf
워드/반워드(halfword)/바이트 단위로 호출하여 16진수/10진수/문자/문자열 형태로 출력할 수 있습니다. 쉼표 다음의 숫자는 오브젝트 카운트입니다. 다음의 0x10 개의 아이템들을 출력하기 위해서는 간단하게 다음의 명령을 수행합니다.
  x ,10
똑같이 다음을 실행시키면
  x/ia foofunc,10
foofunc의 첫번째 10 개의 명령들을 디스어셈블 하고, foofunc의 처음으로부터의 옵셋값에 맞춰 출력을 합니다.

메모리 내용을 수정하려면, 다음의 쓰기 명령을 사용합니다.

  w/b termbuf 0xa 0xb 0
  w/w 0xf0010030 0 0
명령 다음의 접미사(b/h/w)는 쓸 데이타의 크기를 명시하고, 첫번째로 뒤따라오는 수식은 쓸 주소를 나타내며, 나머지는 지정한 메모리 위치에 쓸 데이타로 해석됩니다.

만약 현재 레지스터의 정보에 대해 알고 싶다면, 다음의 명령을 사용합니다.

  show reg
위와 별도로, 다음 예에서와 같이 한 레지스터의 값만 출력할 수 있습니다.
  p $eax
그리고 그 값을 다음과 같이 수정할 수 있습니다.
  set $eax new-value

DDB에서 커널 함수들을 호출해야 한다면, 간단하게 다음과 같이 실행하면 됩니다.

  call func(arg1, arg2, ...)
함수의 반환 값이 출력될 것입니다.

실행되고 있는 모든 프로세스들에 대해 ps(1) 스타일의 요약을 보고 싶다면 다음을 실행하면 됩니다.

  ps

지금까지는 왜 커널의 실행이 실패했는가 진단하는 것이었고, 그 후에는 리부팅을 원할 것입니다. 커널 내부의 오류의 심각성에 따라 커널의 모든 부분이 제대로 동작을 하지 않을 수도 있음을 기억하시기 바랍니다. 시스템을 종료하고 다시 리부팅하기 위해서 다음에 따르는 방식 중 하나를 수행하시기 사랍니다.

  call diediedie()

이 방법은 커널이 코어를 덤프한 다음 리부팅하도록 하며, 나중에 kgdb 등의 고수준의 디버거에서 코어를 분석할 수 있습니다. 이 명령은 `continue' 선언 후에 해야 합니다. 이 동작에 대해 `panic'이라는 앨리어스(alias)가 있읍니다.

  call boot(0)
는 현재 실행 중인 시스템을 깨끗하게 종료하는 훌륭한 방법이며, 모든 디스크에서 sync()를 수행하고, 마지막으로 리부팅을 수행합니다. 커널의 디스크와 파일 시스템 인터페이스가 손상되지 않은 한, 이 방법은 종료하는 좋은 방법입니다.

  call cpu_reset()
비상시에 `적색 상황 버튼'을 누를 때와 같은 심각한 문제에 부딪혔을 때 사용하는 최후의 방법입니다.

명령들에 대한 짧은 요약을 보기 원한다면, 다음의 명령을 실행하면 됩니다.

  help
그러나, ddb(4) 매뉴얼 페이지를 프린트한 복사본을 가지고 디버깅 세션에 대비하는 것을 강력히 추천합니다. 커널을 단계적으로 실행시킬 때 온라인 매뉴얼을 읽는 것은 매우 어려움을 기억하기 바랍니다.


FreeBSD 핸드북 : 커널 디버깅 : DDB를 이용한 온라인 커널 디버깅
Previous: 덤프의 사후 분석(post-mortem)
Next: 원격 GDB를 이용한 온라인 커널 디버깅