디버그

공학에서 디버깅() 또는 디버그()는 버그에 대한 근본 원인, 워크어라운드, 가능한 해결책을 찾는 과정이다.

소프트웨어의 경우 디버깅 전술에는 대화형 디버깅, 제어 흐름 분석, 로그 파일 분석, 애플리케이션 또는 시스템 수준 모니터링, 메모리 덤프, 프로파일링 등이 포함될 수 있다. 많은 프로그래밍 언어 및 프로그래밍 도구는 디버거로 알려진 디버깅을 돕는 프로그램도 제공한다.

일반적으로 디버깅을 하는 방법으로 체크를 통한 테스트, 기계를 사용하는 테스트, 실제 데이터를 사용해 테스트하는 법이 있다.

어원
섬네일|마크 II의 컴퓨터 로그 기록으로, 페이지에 나방이 테이프로 붙어 있다.

결함의 의미에서 '버그'(bug)라는 용어는 적어도 1878년에 토머스 에디슨이 자신의 발명품에서 "작은 결함과 어려움"을 "버그"라고 기록하면서 시작되었다.

1940년대의 인기 있는 이야기는 그레이스 호퍼 제독으로부터 시작된다. 그녀가 하버드 대학교에서 마크 II 컴퓨터를 작업하는 동안, 그녀의 동료들이 작동을 방해하는 나방이 릴레이에 끼어 있는 것을 발견하고 로그북에 "발견된 버그의 첫 실제 사례"라고 기록했다. 비록 언어 유희일 가능성이 높지만, 버그의 두 가지 의미(생물학적 의미와 결함)를 혼동한 이 이야기는 당시 컴퓨터 분야에서 이 용어가 사용되었음을 보여준다.

마찬가지로 '디버깅'이라는 용어는 컴퓨터 세계에 들어오기 전에 항공학에서 사용되었다. 로스앨러모스의 WWII 원자 폭탄 맨해튼 계획의 책임자였던 J. 로버트 오펜하이머가 1944년 10월 27일자 어니스트 로런스 박사에게 보낸 편지에서 추가 기술 직원 채용과 관련하여 이 용어를 사용했다.
옥스포드 영어사전의 디버그 항목은 1945년 왕립 항공 학회 저널 기사에서 비행기 엔진 테스트와 관련하여 디버깅이라는 용어를 사용했다.
"에어포스"(1945년 6월, 50쪽)의 한 기사는 항공기 카메라 디버깅을 언급한다.

1951년 Gill의 중요한 기사는 프로그래밍 오류에 대한 가장 초기의 심층적인 논의이지만, 버그나 디버깅이라는 용어를 사용하지 않았다.

ACM의 디지털 라이브러리에서 '디버깅'이라는 용어는 1952년 ACM 전국 회의의 세 논문에서 처음 사용되었다. 세 논문 중 두 개는 이 용어를 인용 부호 안에 사용했다.

1963년경에는 디버깅이 CTSS 매뉴얼 1페이지에 설명 없이 언급될 정도로 흔한 용어가 되었다.

범위
소프트웨어 및 전자 시스템이 전반적으로 더 복잡해짐에 따라, 다양한 일반적인 디버깅 기술은 이상 징후를 감지하고, 영향을 평가하며, 시스템에 대한 패치 또는 전체 업데이트를 예약하는 더 많은 방법으로 확장되었다. "이상 징후(anomaly)" 및 "불일치(discrepancy)"라는 단어는 소위 오류, 결함 또는 버그가 모두 (어떤 대가를 치르더라도) 수정되어야 한다는 함축이 있을 수 있는 "오류" 및 "결함" 또는 "버그"라는 단어를 피하기 위해 더 중립적인 용어로 사용될 수 있다. 대신, 이상 징후(또는 불일치)를 제거하기 위한 변경 사항이 시스템에 비용 효율적인지 또는 예정된 새 릴리스가 변경 사항을 불필요하게 만들 수 있는지 여부를 결정하기 위해 영향 평가를 수행할 수 있다. 모든 문제가 시스템에서 안전 필수적이거나 임무 필수적인 것은 아니다. 또한, 변경 사항이 알려진 과 함께 사는 것보다 장기적으로 사용자에게 더 큰 혼란을 줄 수 있는 상황("치료가 질병보다 나쁠 수 있는")을 피하는 것이 중요하다. 일부 이상 징후의 허용 가능성에 대한 결정을 내리는 것은 사람들이 문제의 존재를 부인하여 결과적으로 결함이 없는 것처럼 보이게 할 수 있는 "무결함" 명령의 문화를 피할 수 있다. 비용 대 이점 영향 평가와 같은 부수적인 문제를 고려할 때, 광범위한 디버깅 기술은 이상 징후의 빈도(동일한 "버그"가 얼마나 자주 발생하는지)를 결정하여 전체 시스템에 미치는 영향을 평가하는 데 도움을 줄 것이다.

도구

섬네일|right|비디오 게임 콘솔의 디버깅은 일반적으로 개발자를 위한 이 엑스박스 디버그 장치와 같은 특수 하드웨어로 수행된다.

디버깅은 간단한 오류 수정에서부터 데이터 수집, 분석, 업데이트 예약과 같은 길고 지루한 작업에 이르기까지 복잡성이 다양하다. 프로그래머의 디버깅 기술은 문제 디버깅 능력에 중요한 요소가 될 수 있지만, 소프트웨어 디버깅의 난이도는 시스템의 복잡성에 따라 크게 달라지며, 사용되는 프로그래밍 언어와 디버거와 같은 사용 가능한 도구에 따라 어느 정도 달라진다. 디버거는 프로그래머가 프로그램의 실행을 모니터링하고, 중지하고, 다시 시작하고, 브레이크포인트를 설정하고, 메모리 값을 변경할 수 있도록 하는 소프트웨어 도구이다. 디버거라는 용어는 디버깅을 수행하는 사람을 지칭하기도 한다.

일반적으로 자바와 같은 고급 프로그래밍 언어는 예외 처리 및 타입 검사와 같은 기능이 있어 비정상적인 동작의 실제 원인을 더 쉽게 찾을 수 있기 때문에 디버깅을 더 쉽게 만든다. C 또는 어셈블리어와 같은 프로그래밍 언어에서는 버그가 메모리 손상과 같은 침묵하는 문제를 일으킬 수 있으며, 초기 문제가 어디에서 발생했는지 파악하기 어려운 경우가 많다. 이러한 경우에는 메모리 디버거 도구가 필요할 수 있다.

특정 상황에서는 언어에 특화된 범용 소프트웨어 도구가 매우 유용할 수 있다. 이러한 도구는 정적 코드 분석 도구의 형태를 취한다. 이 도구들은 컴파일러 및 인터프리터처럼 구문보다는 의미(예: 데이터 흐름)에 더 중점을 두어 소스 코드 내에서 알려진 특정 문제 집합(일반적인 문제 및 드문 문제 모두)을 찾는다.

다양한 언어에 대해 상업용 및 무료 도구 모두 존재하며, 일부는 수백 가지의 다른 문제를 감지할 수 있다고 주장한다. 이러한 도구는 코드 워크스루를 수행하기 비실용적인 매우 큰 소스 트리를 확인할 때 극도로 유용할 수 있다. 감지되는 문제의 일반적인 예는 변수에 값이 할당되기 전에 발생하는 변수 역참조이다. 또 다른 예로, 일부 이러한 도구는 언어가 요구하지 않을 때 강력한 타입 검사를 수행한다. 따라서 구문적으로는 올바른 코드에서 발생할 수 있는 오류를 더 잘 찾아낸다. 그러나 이러한 도구는 올바른 코드를 의심스러운 것으로 표시하는 오탐(false positive)으로 인해 평판이 좋지 않다. 오래된 유닉스 린트 프로그램이 초기 예이다.

컴퓨터 하드웨어와 같은 전자 하드웨어와 바이오스, 장치 드라이버와 같은 저수준 소프트웨어 및 펌웨어를 디버깅할 때는 오실로스코프, 논리 분석기 또는 인-서킷 에뮬레이터 (ICE)와 같은 계측기가 단독으로 또는 조합하여 자주 사용된다. ICE는 저수준 소프트웨어 및 펌웨어에서 일반적인 소프트웨어 디버거의 많은 작업을 수행할 수 있다.

디버깅 과정
디버깅 과정은 일반적으로 문제를 재현하는 단계를 식별하는 것으로 시작한다. 이는 특히 병렬 프로세스와 일부 하이젠버그의 경우처럼 간단하지 않은 작업일 수 있다. 특정 사용자 환경과 사용 이력도 문제를 재현하기 어렵게 만들 수 있다.

버그가 재현된 후에는 디버깅을 쉽게 하기 위해 프로그램의 입력을 단순화해야 할 수 있다. 예를 들어, 컴파일러의 버그는 큰 소스 파일을 파싱할 때 충돌을 일으킬 수 있다. 그러나 테스트 사례를 단순화한 후에는 원래 소스 파일에서 몇 줄만으로도 동일한 충돌을 재현하기에 충분할 수 있다. 단순화는 분할 정복 접근 방식을 사용하여 수동으로 수행될 수 있는데, 이 접근 방식에서는 프로그래머가 원래 테스트 사례의 일부를 제거한 다음 문제가 여전히 발생하는지 확인한다. GUI에서 디버깅할 때 프로그래머는 원래 문제 설명에서 일부 사용자 상호 작용을 건너뛰어 나머지 작업이 버그를 유발하는 데 충분한지 확인할 수 있다.

테스트 사례가 충분히 단순화된 후, 프로그래머는 디버거 도구를 사용하여 프로그램 상태(변수 값 및 콜 스택)를 검사하고 의 원인을 추적할 수 있다. 또는 트레이싱을 사용할 수 있다. 간단한 경우에 트레이싱은 프로그램 실행 중 특정 지점에서 변수 값을 출력하는 몇 개의 인쇄문일 뿐이다.

기술
 대화형 디버깅은 프로그램의 실행을 한 번에 한 단계씩 처리하고 일시 중지하여 상태를 검사하거나 변경할 수 있도록 하는 디버거 도구를 사용한다. 서브루틴 또는 함수 호출은 일반적으로 전체 속도로 실행될 수 있으며 호출자에게 반환될 때 다시 일시 중지되거나, 단일 단계로 실행되거나, 이러한 옵션의 혼합으로 실행될 수 있다. 결함이 의심되지 않는 코드를 전체 속도로 실행하고 특정 지점에서 중지하도록 설정 지점을 설치할 수 있다. 프로그램 루프의 끝 바로 뒤에 설정 지점을 두는 것은 반복 코드를 평가하는 편리한 방법이다. 일반적으로 실행이 특정 변수가 변경될 때까지 진행될 수 있는 감시점과 예외 또는 공유 라이브러리 로딩과 같은 특정 종류의 프로그램 이벤트에 대해 디버거를 중지시키는 캐치포인트가 있다.
 또는 트레이싱은 프로세스의 실행 흐름과 데이터 진행을 나타내는 추적 문 또는 인쇄 문을 (실시간 또는 기록된) 관찰하는 행위이다. 트레이싱은 특수 도구(예: GDB의 트레이스)를 사용하거나 소스 코드에 추적 문을 삽입하여 수행할 수 있다. 후자는 C의 Printf 함수를 사용하기 때문에 이라고도 불린다. 이러한 종류의 디버깅은 초보자 지향 베이직 프로그래밍 언어의 초기 버전에서 TRON 명령으로 켜졌다. TRON은 "Trace On"의 약자였다. TRON은 프로그램이 실행될 때 각 베이직 명령 줄의 줄 번호를 인쇄하게 했다.
 활동 트레이싱은 (위의) 트레이싱과 유사하지만, 한 번에 한 명령이나 함수씩 프로그램 실행을 따르는 대신, 프로세서/CPU가 특정 코드 세그먼트를 실행하는 데 소비한 전체 시간을 기반으로 프로그램 활동을 따른다. 이는 일반적으로 정의된 메모리 주소(기계 코드 프로그램) 또는 특정 프로그램 모듈(고급 언어 또는 컴파일된 프로그램) 내에서 명령을 처리하는 데 소비된 프로그램 실행 시간의 비율로 표시된다. 디버깅 중인 프로그램이 트레이싱된 영역 내에서 실행 시간의 지나치게 많은 부분을 소비하는 것으로 나타나면, 이는 잘못된 프로그램 논리로 인한 프로세서 시간의 잘못된 할당 또는 최소한 최적화 노력으로 이점을 얻을 수 있는 비효율적인 프로세서 시간 할당을 나타낼 수 있다.
 은 디버거와 다른 시스템에서 실행 중인 프로그램을 디버깅하는 프로세스이다. 원격 디버깅을 시작하려면 디버거가 근거리 통신망과 같은 통신 링크를 통해 원격 시스템에 연결한다. 그런 다음 디버거는 원격 시스템에서 프로그램의 실행을 제어하고 해당 상태에 대한 정보를 검색할 수 있다.
 사후 디버깅은 프로그램이 이미 충돌한 후에 프로그램을 디버깅하는 것이다. 관련 기술에는 로그 파일 검사, 충돌 시 콜 스택 출력, 그리고 충돌한 프로세스의 메모리 덤프 (또는 코어 덤프) 분석과 같은 다양한 트레이싱 기술이 포함된다. 프로세스 덤프는 시스템에 의해 자동으로 (예를 들어, 처리되지 않은 예외로 인해 프로세스가 종료되었을 때) 또는 프로그래머가 삽입한 지시사항에 의해, 또는 대화형 사용자에 의해 수동으로 얻을 수 있다.
 "늑대 울타리" 알고리즘: 에드워드 가우스는 1982년 커뮤니케이션스 오브 더 ACM 기사에서 이 간단하지만 매우 유용하고 이제 유명한 알고리즘을 다음과 같이 설명했다. "알래스카에 늑대 한 마리가 있다. 어떻게 찾을까? 먼저 주 중간에 울타리를 세우고, 늑대가 울기를 기다리고, 울타리의 어느 쪽에 있는지 확인한다. 늑대를 볼 수 있는 지점까지 해당 쪽에서만 과정을 반복한다." 이것은 예를 들어 Git 버전 관리 시스템에서 `git bisect` 명령으로 구현되는데, 이 명령은 위의 알고리즘을 사용하여 특정 커밋에서 특정 버그가 도입되었는지 여부를 결정한다.
 기록 및 재생 디버깅은 프로그램 실행 기록(예: Mozilla의 무료 rr 디버깅 도구를 사용하여; 가역 디버깅/실행 가능)을 생성하는 기술로, 이를 재생하고 대화식으로 디버깅할 수 있다. 원격 디버깅 및 간헐적, 비결정적, 기타 재현하기 어려운 결함을 디버깅하는 데 유용하다.
 시간 여행 디버깅은 컴퓨터 프로그램 실행 중에 무슨 일이 일어나는지 이해하기 위해 소스 코드를 통해 시간을 거슬러 올라가는 과정(예: Undo LiveRecorder 사용)으로, 사용자가 프로그램과 상호 작용하고, 원한다면 기록을 변경하고, 프로그램이 어떻게 반응하는지 볼 수 있도록 한다.
 델타 디버깅 – 테스트 케이스 단순화를 자동화하는 기술이다.
 Saff Squeeze – 실패한 테스트의 일부를 점진적으로 인라인하여 테스트 내에서 실패를 격리하는 기술이다.
 인과 관계 추적: 계산에서 인과 관계 사슬을 추적하는 기술이 있다. 이러한 기술은 널 포인터 역참조와 같은 특정 버그에 맞게 조정될 수 있다.
자동 버그 수정

임베디드 시스템 디버깅
범용 컴퓨터 소프트웨어 설계 환경과 달리 임베디드 환경의 주요 특징은 개발자가 사용할 수 있는 다양한 플랫폼(CPU 아키텍처, 공급업체, 운영 체제 및 해당 변형)이 엄청나게 많다는 것이다. 임베디드 시스템은 정의상 범용 설계가 아니다. 일반적으로 단일 작업(또는 소규모 작업 범위)을 위해 개발되며, 플랫폼은 해당 애플리케이션을 최적화하기 위해 특별히 선택된다. 이러한 사실은 임베디드 시스템 개발자에게 어려운 점을 만들 뿐만 아니라, 다른 플랫폼에 다른 디버깅 도구가 필요하므로 이러한 시스템의 디버깅 및 테스트도 더 어렵게 만든다.

위에 언급된 이질성의 어려움에도 불구하고, 일부 디버거는 상업적으로 개발되었으며 연구 프로토타입도 있다. 상업적 솔루션의 예로는 그린 힐스 소프트웨어, 라우터바흐 GmbH 및 마이크로칩의 MPLAB-ICD(인-서킷 디버거용)가 있다. 연구 프로토타입 도구의 두 가지 예는 Aveksha 및 Flocklab이다. 이들은 모두 저가형 임베디드 프로세서에서 사용할 수 있는 기능인 온칩 디버그 모듈(OCDM)을 활용하며, 그 신호는 표준 JTAG 인터페이스를 통해 노출된다. 이들은 애플리케이션에 필요한 변경량과 추적할 수 있는 이벤트 속도를 기준으로 벤치마킹된다.

시스템에서 버그를 식별하는 일반적인 작업 외에도 임베디드 시스템 디버깅은 시스템의 작동 상태에 대한 정보를 수집하여 시스템을 분석하는 데 사용될 수 있다. 즉, 성능을 향상시키거나 다른 중요한 특성(예: 에너지 소비, 신뢰성, 실시간 응답 등)을 최적화하는 방법을 찾는 것이다.

안티 디버깅
안티 디버깅은 "타겟 프로세스를 역공학하거나 디버깅하려는 시도를 방해하는 하나 이상의 기술을 컴퓨터 코드 내에 구현하는 것"이다. 이는 복사 방지 스키마에서 공인된 퍼블리셔에 의해 적극적으로 사용되지만, 악성 소프트웨어에 의해서도 탐지 및 제거를 복잡하게 만들기 위해 사용된다. 안티 디버깅에 사용되는 기술은 다음과 같다.
 API 기반: 시스템 정보를 사용하여 디버거 존재 여부 확인
 예외 기반: 예외가 방해받는지 확인
 프로세스 및 스레드 블록: 프로세스 및 스레드 블록이 조작되었는지 확인
 수정된 코드: 소프트웨어 브레이크포인트를 처리하는 디버거에 의해 만들어진 코드 수정 사항 확인
 하드웨어 및 레지스터 기반: 하드웨어 브레이크포인트 및 CPU 레지스터 확인
 타이밍 및 지연: 명령 실행에 걸리는 시간 확인
 디버거 감지 및 처벌

안티 디버깅의 초기 예는 마이크로소프트 워드의 초기 버전에 존재했다. 이 버전은 디버거가 감지되면 "악의 나무는 쓴 열매를 맺는다. 이제 프로그램 디스크를 휴지통에 버린다."라는 메시지를 생성한 후 플로피 디스크 드라이브가 alarming 소음을 내도록 하여 사용자가 다시 시도하는 것을 겁주려 했다.

 같이 보기 

 표명
 디버깅 패턴
 매직 디버그 값
 샷건 디버깅
 소프트웨어 버그
 소프트웨어 테스트
 시간 여행 디버깅
 추적 테이블
 트러블슈팅

 각주