Joyful Project/linux

리눅스 개발자를 위한 디버깅 기법 ②

deity4u 2010. 5. 19. 15:05
코어 파일, gdb와 gdbserver

박재호
[ 입력 : 2006-04-04 오후 5:45:56 | 지면발행 : 2006년 4월호 76쪽]



부트윈


BW-1st Product - Low cost Product (STB, POS, Medical, Game, DVR) Mini-ITX Solution, Fast IDE Flash, Embedded System Develop


대화식 디버거가 존재하지 않던 시절에는 콘솔 출력이나 UART 출력으로 필요한 값을 화면에 뿌려서 문제 위치를 파악하는 방법을 주로 많이 사용했었다. 물론 요즘도 디버거를 사용할 수 없는 환경에서 여전히 이런 방법을 동원하기도 하지만, 생산성을 높이기 위해서는 대화식 디버거를 사용해야 한다. 대화식 디버거는 원시 코드를 보면서 프로그램을 추적하는 기능을 제공하기에 좀 더 시각적으로 다가갈 수 있기 때문이다. 대화식 디버거는 문제 해결뿐만 아니라 처음으로 분석하는 소프트웨어 내부를 탐험하는 과정에서도 상당한 도움을 준다. 실행 도중에 내부 상황을 모두 확인할 수 있기 때문이다. 이번 연재 글에서는 x86 리눅스부터 시작해서 PowerPC 매킨토시 맥 OS X에 이르기까지 다양한 플랫폼과 운영체제에서 실행되는 gdb를 소개하겠다.

들어가는 말

유닉스와 유사한 어떤 운영체제가 있을 때 여기에 배시 셸과 gcc를 이식하면 절반이 끝났다고 말한다. 하지만 디버깅을 위한 gdb까지 이식해야지만 정말로 절반이 끝났다고 볼 수 있다. 통합 IDE 환경이거나 vi-make-gcc(아니면 당신이 사용하는 GNU 컴파일러)-gdb로 이어지는 순수한 개발 환경이던 상관없이, gdb는 소프트웨어 문제 해결 과정에서 도움을 주는 강력한 개발 도구이므로 gdb의 사용법에 대해서는 확실하게 꿰뚫고 있어야 한다.
2회에서는 유닉스와 리눅스 세계에 들어오면 가장 먼저 의문을 품게 되는 코어 파일 설명을 시작으로 gdb와 임베디드 환경에서 대화식 디버거를 사용하도록 만드는 gdbserver를 소개하며, 마지막으로 gdb 그림 사용자 인터페이스인 DDD와 Insight를 소개하겠다. 가장 먼저 gdb의 이모저모에 대해 상위 단계에서 살펴보기로 하자.

gdb 이모저모

영문 위키피디아에서 gdb를 찾아보면 다음과 같은 짤막한 설명을 볼 수 있다.
「GNU 디버거이며, 간략하게 줄여서 GDB라고 불리는 이 소프트웨어는 GNU 소프트웨어 시스템을 위한 표준 디버거이다. GDB는 다양한 유닉스 계열 시스템에서 동작하며, C, C++, 포트란을 비롯한 수많은 프로그래밍 언어를 디버깅하도록 도와주는 이식성 높은 디버거이다.」
여기서 ‘이식성 높은’이라는 단어에 한 번 집중해보자. gdb는 알파, ARM, x86, x86-64, IA-64, 모토롤라 68000, MIPS, PA-RISC, 파워PC, SuperH, Sparc과 같은 다양한 CPU에 이식되어 있으며, gdb 컴파일러로 컴파일한 C, C++, 파스칼, Objective C, 자바, 포트란과 같은 다양한 프로그래밍 언어에 대한 디버깅을 지원하고 있다. 일반적인 유닉스 운영체제는 물론이고 Cygwin에서 동작하도록 이식되어 있으므로 유닉스뿐만 아니라 윈도우 환경에서도 사용할 수 있다.

코어(core) 파일이란?

처음 유닉스 계열 프로그램을 작성하는 도중에 누구나 한 번씩은 코어 파일을 만들어본 경험이 있을 것이다. 코어는 과거 메인 프레임 시절에 메모리를 구성하는 소자에서 따온 말이며, 코어 파일은 응용 프로그램이 여러 가지 원인으로 인해 수행이 불가능한 상황에 이를 경우 문제가 생긴 코어 내용을 그대로 덤프한 파일이다. TLDP 문서(http://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/glossary.html) 정의에 따르면 코어 파일은 다음과 같다.
「코어 파일은 버그나 기타 운영체제나 하드웨어 보호 매커니즘을 위반하여 비정상적으로 프로그램이 종료할 때 만들어진다. 운영체제는 문제가 발생한 응용 프로그램을 죽이며, 코어 파일을 만들어 프로그래머가 무엇이 잘못되었는지를 판단하도록 도와준다. 코어 파일은 프로그램이 죽었을 시점에서 프로그램 상태가 어떤지를 세부적으로 기술하는 정보를 포함한다. 어떤 프로그램이 코어 파일을 만들었는지 알고 싶다면 file 명령어를 사용하면 된다. 코어 덤프를 일으킨 명령어를 알아내면, 해당 프로그래머를 관리하는 사람에게 core를 제출하는 방법으로 문제점을 알려줄 수 있다.」
코어라는 이름이 주는 묘한 중요성 때문에 병아리 개발자들은 코어 파일을 응용 프로그램이 수행하는 과정에 반드시 필요한 핵심 파일로 착각하고 백업을 받아두기까지 한다. 심지어 개발자 입장일 경우에도 착각이 일어나는 판국이니, 일반 사용자 입장에서는 코어 파일이 무척 성가신 존재인 셈이다. 따라서 일반 리눅스 배포판은 코어 파일이 만들어지지 않도록 코어 크기를 0으로 만들어 놓았다.
하지만 개발 과정에서 디버깅을 위해 코어 파일이 반드시 필요하므로 코어 파일 크기와 각종 환경 설정 작업이 필요하다. http://www.novell.com/coolsolutions/feature/16257.html를 참조해서 몇 가지 유용한 사항을 정리해보았다.

코어 파일 크기 확인과 조정: ulimit 명령을 사용해서 코어 파일 크기를 확인하고 변경할 수 있다.

# ulimit -c -> 코어 파일 크기를 확인한다
# ulimit -c 500000 -> 코어 파일 크기를 500000 바이트로 설정한다
# ulimit -c unlimited -> 코어 파일 크기를 무제한으로 설정한다

코어 파일 이름 설정: /proc 파일 시스템을 활용해서 코어 파일 뒤에 프로세스 식별자를 붙일 수 있다.

# echo 1 > /proc/sys/kernel/core_uses_pid

또한 /proc 파일 시스템을 활용해서 이름 패턴을 정의할 수도 있다. /proc/sys/kernel/core_pattern에 %p(프로세스 식별자), %u(덤프된 프로세스의 진짜 사용자 식별자), %g(덤프된 프로세스의 진짜 그룹 식별자), %s(덤프를 초래한 신호 번호), %t (ephoc 기준으로 덤프 시각), %h(호스트 이름), %e(실행 파일 이름)를 넣으면 코어 파일 이름을 관리가 편한 방법대로 바꿀 수 있다. 예를 들면 다음과 같이 코어 파일 이름을 실행 파일 이름-프로세스 식별자-덤프 시각으로 바꾼다.
# echo core-%e-%p-%t > /proc/sys/kernel/core_pattern

코어 파일은 gdb와 같은 디버거에서 인식하기 때문에 응용 프로그램이 비정상적인 상황으로 종료됐을 때 디버깅 정보가 붙어있는 실행 파일인 경우 gdb에서 코어 파일을 읽기만 하면 문제가 발생한 정확한 지점과 스택 역추적 정보를 바로 확인할 수 있다. 디버깅 정보가 붙어있지 않은 실행 파일일 경우에는 아쉽지만 소스코드가 아니라 역 어셈블 목록을 출력하므로 디버깅 정보를 붙여서(예: gcc의 -g 옵션) 다시 한 번 컴파일한 다음에 문제를 재연해야 한다.

gdb 활용하기

지금부터는 간략하게 기본적인 gdb 활용방안을 살펴보기로 하자. 앞서 언급한 바와 같이 gdb를 사용하기 전에 반드시 -g 옵션을 사용해서 다음 예와 같이 프로그램을 컴파일해야 한다. -g 옵션을 붙이지 않으면 심볼릭 정보가 프로그램에 포함되지 않으므로 gdb를 사용해서 원시 코드 단계에서 디버깅을 할 수 없다.

$ gcc -g -o my_prog myprog.c

정지점 설정
지정한 지점(정지점)에서 프로그램 실행을 일시적으로 중단하는 기능이다. 정지점에서 프로그램을 중단한 다음에 다른 gdb 명령을 사용하면 된다. 정지점 설정에 사용하는 명령어는 break(줄여서 b), condition(조건부 정지점) 등이 있다.
break 명령어는 함수를 매개변수로 받아들여서 해당 함수를 호출하는 시점에서 멈추도록 만든다. 다음 예는 main 함수에서 gdb가 수행을 멈추도록 지시한다. (gdb)는 gdb 프롬프트를 의미하므로 타이핑하면 안된다.

(gdb) break main
condition 명령어는 정지점 번호와 조건을 매개변수로 받아들여서 조건부로 정지점에 멈추도록 만든다. 다음 예는 day가 365가 될 경우 정지점 2번에 멈추도록 만든다.
(gdb) condition 2 day == 365

정지점 정보를 보고 싶으면 info breakpoint 명령을 내리면 된다. 정지점 번호와 정지점 주소를 출력한다.

(gdb) info breakpoint

하드웨어 감시점
일부 프로세서에서는 하드웨어를 사용하여 특정 메모리값이 변하는지 감시할 수 있다. 이 과정은 하드웨어가 수행하므로, 메모리값이 변할 때까지 프로그램은 정상 속력으로 동작한다. 하드웨어 감시점 설정에 사용하는 명령어는 watch가 있다.
watch 명령어는 변수 이름을 매개변수로 받아들여서 해당 변수가 변할 때 해당 값을 출력하며 멈춘다. 다음 예는 day라는 변수값이 바뀌는 시점에서 멈추도록 만든다.

(gdb) watch day

프로그램 실행
중단점을 걸었을 때 계속해서 프로그램을 수행하거나 단계적으로 프로그램을 실행해야 한다. 이렇게 프로그램 실행 과정에서 사용하는 명령어는 run(줄여서 r), next(줄여서 n), step(줄여서 s), continue(줄여서 c), finish 등이 있다.
run 명령어는 프로그램을 처음부터 시작한다.

(gdb) run

next 명령어는 다음 소스 코드 행을 실행하는데, 함수 내부로는 진입하지 않는다. next 뒤에 숫자를 넘기면, 해당 숫자만큼 next를 반복한다.

(gdb) next

step 명령어는 다음 소스 코드 행을 실행하는데, 함수 내부로 진입한다. 역시 step 뒤에 숫자를 넘기면 해당 숫자만큼 step을 반복한다.

(gdb) step

continue 명령어는 실행을 복귀하는 명령이다. 다음 정지점을 만날 때까지 계속 수행한다.

(gdb) continue

finish 명령어는 현재 스택 프레임이 끝날 때까지만 계속 수행한다. 즉 들어간 함수 내부 실행이 끝날 때까지 유효하다.

(gdb) finish

변수값 출력
현재 스택 프레임에서 유효한 변수값을 출력한다. 다양한 용법을 제공하는 print 명령을 사용한다. day 변수값을 16진수로 출력하려면 다음과 같이 /x 매개변수를 넘긴다.

(gdb) print /x day

변수나 문자열을 포매팅해서 보려면 printf와 유사한 포매팅 문자열을 사용한다.

(gdb) print “%s ”, const_str

직전에 print 명령으로 출력한 결과를 재사용하려면 $1, $2와 같은 $ 표기법을 사용한다. $값은 print 명령 수행 직후에 결과를 출력하는 과정에서 확인이 가능하다.

(gdb) print $1 + 300

여기서 초보자가 한 가지 주의할 사항이 있는데, print 문으로는 매크로로 치환된 값을 보지 못한다. 따라서 매크로 값을 확인하려면 소스 코드를 뒤져야 하는 불편함이 있다. 이런 문제를 근본적으로 해결하려면 매크로 치환 대신에 const를 사용한 상수 선언을 사용해야 한다.

스택 추적
스택은 프로세스가 실행 중에 코드 어디에 위치하는지를 알려주는 정보를 포함하고 있으므로 매우 중요하다. 현재 수행 위치를 파악하려면 반드시 스택 정보를 사용해야 하며, 다행히도 gdb는 스택 추적에 필요한 여러 명령어를 제공하고 있다. 많이 사용하는 명령은 backtrace(줄여서 bt), frame, up, down 등이 있다.
backtrace 명령어는 현재 디버깅중인 프로세서에 대한 스택 추적을 덤프해준다. backtrace 명령 결과는 함수 호출 체인을 보여주므로, 어떤 경로를 통해 현재 함수를 호출했는지 한눈에 살펴볼 수 있다. 코어 파일을 읽은 직후에 backtrace 명령을 한 번 내려주면 코어가 발생한 지점을 바로 출력한다. 참고로 backtrace에서 출력하는 정보에는 각 함수마다 매개변수로 넘어간 값이 포함되어 있으므로 디버깅 과정에서 일일이 개별 함수 호출에 따른 매개변수 점검 과정을 생략할 수 있다.

(gdb) backtrace

frame 명령어는 현재 스택 프레임을 다른 곳으로 이동하는 데 사용한다. C와 같은 언어는 현재 함수 내부의 변수만 보이므로(물론 전역변수는 어느 함수에서도 보인다), 특정 함수 내부에 존재하는 변수를 살펴보기 위해서는 스택 프레임 이동이 필수적이다. frame 명령어 뒤에 프레임 번호를 붙이면 해당 프레임으로 바로 이동하며, 이 프레임 번호는 backtrace 명령어 결과에서 확인이 가능하다.

(gdb) frame 2

프레임을 한 단계씩 위아래로 오르내리도록 만들려면 up과 down을 사용한다.

(gdb) up
(gdb) down

gdb는 절대로 스택 처음과 끝을 지나치도록 허용하지 않으므로 스택 오버플로우나 스택 언더플로우 걱정은 할 필요가 전혀 없다. 마지막으로 스택 정보를 확인하려면 만능 재주꾼인 info를 다음과 같이 사용한다.

(gdb) info frame 2

코어 읽기와 쓰기
코어 파일이 만들어졌을 경우 gdb를 사용해서 이 코어 파일을 읽을 수 있다. 가장 손쉬운 방법으로 gdb 명령을 내리면서 코어 파일 이름을 지정하면 된다.

$ gdb my_prog core

여기서 core는 코어 파일 이름이며, 만일 core_pattern을 사용해서 코어 이름을 변경했다면 바뀐 코어 파일 이름을 지정해야 한다.
gdb를 사용해서 현재 디버깅 중인 프로세스의 상태를 덤프할 수 있다. gdb 내장 명령어인 generate-core-file를 사용하면 현제 실행 중인 디버깅 세션을 그대로 코어 파일로 만들어준다. 이런 기능은 불가피한 사유로 디버깅 도중에 컴퓨터를 꺼야 할 때 위력을 발휘한다. 나중에 전원을 넣고 컴퓨터를 시동한 상태에서 디버깅을 위한 gdb 세션을 준비할 때 저장했던 코어 파일을 읽으면 직전 디버깅 상태 그대로 복원이 가능하기 때문이다.
(gdb) generate-core-file

gdbserver

임베디드 환경에서는 하드웨어적인 디버거를 사용하지 않으면 소프트웨어적인 프로그램 추적이 불가능하므로, 임베디드 개발자들은 프로그램 개발 초기를 제외하고 디버거 사용을 꺼려하는 경향이 있다. 디버깅은 주로 UART와 같은 직렬 통신을 사용한 상태 출력(일반 프로그램에서 printf로 조건을 콘솔로 출력하며 디버깅하는 작업과 유사하다)에 의존한다.
임베디드 리눅스 환경에서는 일반 리눅스 개발 환경에서 사용하는 디버깅 환경(최강의 디버거인 gdb)을 그대로 쓸 수 있으므로 상당한 강점으로 작용한다. 물론 gdb 자체를 교차 컴파일해서 목표 컴퓨터로 가져간 다음 실행할 수도 있다. 하지만 gdb가 상당히 덩치가 크며 메모리를 많이 잡아 먹기 때문에 교차 디버깅이라는 좀더 효과적인 방법을 고안했다. gdb를 교차 디버깅이 가능하도록 컴파일한(주의: 이 때 교차 gdb는 일반적인 교차 개발 환경과 마찬가지로 호스트 컴퓨터에서 동작한다) 다음에 호스트 컴퓨터에서 돌리며, gdbserver라는 작은 프로그램을 목표 컴퓨터에서 돌리면 gdb가 gdbserver에 필요한 명령을 내리고 gdbserver는 이 명령에 따라 수행한 결과를 gdb에 되돌려준다. 호스트 컴퓨터에서는 마치 지역 컴퓨터에서 프로그램을 돌리는 느낌으로 디버깅할 수 있다. 목표 보드 쪽에서 수행하는 프로그램은 심지어 공간을 줄이기 위해 strip 명령으로 디버깅에 필요한 심볼 정보를 빼버려도 된다. 이런 특질은 심볼 정보가 상당히 큰 c++ 프로그래밍 언어로 개발한 프로그램을 디버깅할 때 특히 유용하다.
그림 1에 교차 디버깅 환경을 예시하였다. 그림에서 나타난 바와 같이 gdb와 gdbserver는 직렬 통신이나 TCP/IP를 사용해서 통신하므로, 교차 디버거를 사용하기 위해서는 반드시 목표 컴퓨터에 직렬이나 네트워크 장치를 연결해야 한다. 포트 번호를 양쪽에서 똑같이 지정한 다음에 gdb와 gdbserver를 동작시키기만 하면 자동으로 연결이 이뤄진다.
gdb와 gdbserver를 사용하려면 클라이언트/서버 사이 통신을 위해 TCP/IP 네트워크나 직렬 통신 설정이 필요하다. 여기서는 TCP/IP 네트워크를 사용한다고 보고 192.168.114.1을 개발 호스트로, 192.168.114.2를 목표 컴퓨터로 가정한다. 물론 gdbserver와 교차 컴파일한(x86 환경이라면 일반적으로 컴파일한) 프로그램은 교차 컴파일 후 목표 컴퓨터로 미리 복사해 놓아야 한다. 다음과 같은 방법으로 목표 보드와 개발 호스트쪽 디버깅 환경을 설정하자.

● 목표 보드쪽 설정: 목표 보드 쪽에서는 gdbserver만 띄워놓으면 모든 작업이 끝난다. 여기서 미리 네트워크 설정을 잡아놓아야 한다는 사실을 기억하기 바란다.

[jrogue@remote gdbserver]$ ./gdbserver 192.168.114. 1:9012 debug
Process debug created; pid = 1028

- 포트 번호 9012는 시스템에서 사용하지 않는 임의로 정한 값이다. 시스템 관리자가 아닌 일반 사용자는 반드시 포트 번호를 1024 이상으로 지정해야 한다.
- pid 값은 수행할 때마다 달라질 수 있다.
- 직렬 포트를 통해 디버깅을 하려면 다음과 같이 명령을 내린다. /dev/ttyS0는 상황에 맞춰서 변경하기 바란다.
$ ./gdbserver /dev/ttyS0 debug

● 개발 호스트쪽 디버깅: 개발 호스트쪽에서는 gdb를 사용해서 실제 디버깅 작업을 벌인다. gdb 프롬프트가 떨어지면 다음과 같은 명령을 내려서 디버깅 서버에 접속하자.

(gdb) target remote 192.168.114.2:9012
192.168.114.2:9012: Success

- “target remote 192.168.114.2:9012”에서 9012는 직전에 gdbserver에서 정의한 포트 번호이다.
- 직렬 포트를 통해 디버깅을 하려면 다음과 같이 target 명령을 내린다. 앞서 gdbserver와 마찬가지로 /dev/ttyS0는 상황에 맞춰서 변경하기 바란다.
(gdb) target remote /dev/ttyS0

gdbserver는 gdb 패키지 내부에 들어있으므로, 별도로 내려받을 필요는 없다. 교차 개발 환경에 맞춰서 컴파일만 해주면 끝난다. 또한 gdbserver에서도 웬만한 gdb 명령은 똑같은 방식으로 내릴 수 있으므로, gdb에 익숙한 개발자라면 처음 시작할 때 내리는 몇 가지 명령어를 제외하고는 다른 특별한 내용을 배울 필요가 없다.

DDD와 인사이트(Insight)

윈도우 환경에서 IDE를 사용하다가 처음으로 순수 gdb를 보면 저절로 비명이 튀어나오기 마련이다. 전형적인 CUI(Charac- ter User Interface)를 따르고 있기에 외부에서 터미널 연결만 가능하면 언제 어디서나 디버깅 작업을 진행할 수 있다는 장점이 있긴 하지만, gdb는 모든 명령어를 키보드로 입력해야 하기 때문에 익숙해지기까지 상당한 시간을 요한다. 이런 문제점을 해결하기 위해 오픈 소스 개발자들은 gdb와 통신하면서 필요한 기능을 마우스와 아이콘으로 수행 가능하도록 만들어주는 그림 사용자 인터페이스를 덧붙이는 프론트 엔드를 개발하는 작업을 시작했다.
gdb 프론트 엔드 중에 초창기에 나온 소프트웨어로 xxgdb를 빼놓을 수 없다. xxgdb는 gdb가 나오기 전에 한 시절을 풍미했던 dbx의 프론트 엔드인 xgdb를 gdb용으로 만든 프론트 엔드이며, X11/Xt의 표준 위젯 집합인 아데나를 사용하고 있으므로 요즘 보면 상당히 촌스러운 느낌이 난다. 몇몇 gdb 프론트 엔드 변종이 있었지만, 모티프를 기반으로 획기적인 사용자 인터페이스로 무장한 DDD가 등장하면서 gdb 프론트 엔드 소프트웨어를 하나로 통일해버리게 된다.
DDD는 모티프 라이선스 문제로 초기에는 상용 모티프 라이브러리를 장착한 워크스테이션 급 컴퓨터에서만 돌아갔다(계속해서 xxgdb 개발이 이뤄졌다). 하지만 모티프 클론인 레스티프가 등장하고 모티프 자체도 오픈 소스로 풀리면서 오픈 모티프로 거듭났기 때문에 요즘에는 리눅스와 같은 공개 소스 운영체제에서도 동작한다. DDD는 Data Display Debugger라는 이름에 걸맞게 자료 구조 시각화에 상당한 위력을 발휘한다. 자료 구조를 시각적인 다이어그램으로 보여주므로 연결 리스트와 같은 복잡한 자료 구조나 그래프도 종이와 연필을 사용하지 않고 컴퓨터 화면에서 바로 파악이 가능하다. 아래 소개하는 그림을 보면 알겠지만 복잡한 자료구조를 알기 쉽게 그림으로 보여준다.
모티프 인터페이스를 기반으로 만들었으며 gdb 프론트 엔드로 동작하는 DDD와 달리 인사이트는 Tcl/Tk를 사용해서 새롭게 만든 gdb 변종이다. 인사이트는 Cygwin에서 gdb를 동작시키기 위해 시작한 프로젝트이며 레드햇과 시그너스에서 개발하고 있다. Cygwin만이 Tcl/Tk를 돌릴 수 있는 환경으로 이식 가능하지만 독립적으로 동작하는 특성상 주로 Cygwin에서 많이 사용하고 있다.
DDD나 인사이트 모두 복잡한 gdb 명령어를 암기할 필요없이 다중 윈도우 환경에서 메뉴,아이콘과 마우스만으로 필요한 gdb 명령을 내릴 수 있으며, 필요하다면 gdb 콘솔 창을 열어서 직접 gdb 명령을 내리거나(예: 임베디드 디버깅을 위한 gdb- server와 연결하기 위한 명령), 그림 사용자 인터페이스로 내린 명령이 어떻게 진짜 gdb 명령으로 탈바꿈하는지 콘솔 창에서 직접 확인할 수도 있다.

끝 말

gdb 명령어 종류는 워낙 많고 활용법도 다양하므로 한정된 지면에서 모든 내용을 소개하기란 불가능에 가깝다(실제 PDF로 만든 공식 GDB 매뉴얼은 400페이지를 가뿐하게 넘어선다). 따라서 이번 연재에서는 기본적인 gdb 활용법과 임베디드 환경에서 gdb를 활용하는 방법에 집중했다. 좀 더 고급 정보가 필요하다면 참고 문헌에 나와 있는 다양한 정보를 활용하기 바란다.
아무리 도구가 좋더라도 결국 디버깅은 사람 머리로 수행하는 작업이므로 gdb 사용법에 너무 집착해서 정작 중요한 디버깅 본질을 놓치면 곤란하다. 리눅스 토발즈를 커널 내부에 탑재한 디버거가 커널 소스 코드 개발자에게 편법을 조장하거나 새로운 기능을 추가한 코드에 대한 이해도를 떨어뜨리는 도구로 전락할까 두렵다(‘리눅스 디버깅과 성능 튜닝’ 13장 커널 디버거 참조)고 지적했듯이 디버거는 잘못 사용하면 독이 될 가능성이 높다. 따라서 디버거는 코드를 충분히 이해한 다음에 디버깅 생산성을 높이기 위해 사용해야지 처음부터 코드가 어떤 일을 하는지도 모르는 상태에서는 막무가내로 사용하지 않기 바란다.