Effective Debugging/Chapter 1. 고차원 전략

created : 2020-04-07T11:44:40+00:00
modified : 2020-09-26T14:26:50+00:00

Item 1. Issue Tracking (모든 문제를 이슈 추적 시스템으로 관리하기)

  • Github, GitLab
  • JIRA, Bugzilla, Launchpad, OTRS, Redmine, Trac

장점

  • 디버깅 작업 과정을 명확하게 파악할 수 있다.
  • 릴리즈 일정을 수립하게 추적할 수 있다.
  • 작업의 우선순위를 정할 수 있다.
  • 자주 발생하는 이슈나 해결책을 문서로 정리할 수 있다.
  • 해결해야 할 문제를 실수로 빼먹지 않을 수 있다.
  • 릴리즈 노트를 자동으로 생성할 수 있다.
  • 결함을 측정하고, 이를 되돌아보며 교훈을 얻을 수 있는 저장소로 활용할 수 있다.

문제를 재현하는 방법을 기록해라

버그리포트 작성법

  • 정확한 제목
  • 버그의 우선순위와 심각한 정도
  • 해당 버그에 관련된 이름
  • 버그가 발생한 환경에 대한 상세 정보

장단점

  • 제목을 정확하고 간결하게 작성하면 요약 리포트만 봐도 어떤 버그인지 쉽게 알아볼 수 있다. 가장 나쁜 예는 프로그램이 갑자기 뻗었음과 같이 적는 것이다. 저장하는 동안 리프레시 버튼을 누르면 프로그램이 뻗음과 같이 구체적으로 적는 것이 바람직하다.
  • 심각한 정도를 명시하면 버그의 우선순위를 정하는 데 도움이 된다. 데이터가 손실되는 버그는 당연히 심각한 문제로 봐야하지만, 미적인 문제나 대안이 알려진 문제는 이보다 심각한 정도가 낮다. 이처럼 버그의 심각한 정도를 명시하면 이슈 목록을 작성할 때 당장 해결해야 하는 이슈, 나중에 처리해도 되는 이슈, 무시해도 되는 이슈 등과 같이 우선순위에 따라 분류할 수 있다.
  • 이렇게 정해진 우선순위는 이슈의 우선순위 항목에 기록한다. … 책에 내용이 더 있지만 굳이 전부 적는건 필요 없고 여기까지가 필요한 정보이니 여기까지만

기억할 사항

  • 모든 문제를 이슈 추적 시스템으로 처리한다.
  • 이슈를 작성할 때 문제를 재현하는 방법으로 정확하고, 간결하고, 관련된 사항을 모두 담아서, 구체적인 예제와 함께 기록한다.
  • 이슈의 심각한 정도와 우선순위를 정해서 이에 맞게 작업 일정을 짠다.
  • 이슈 추적 시스템을 통해 작업 현황을 문서화 한다.

Item 2. 해결책을 웹에서 검색할 때 검색어를 구체적으로 표현하기

SSCCE(short, self-contained, correct (compilable and runnable) example)

기억할 사항

  • 발생한 에러에 대한 해결책을 웹에서 검색할 때 에러 메시지를 큰따옴표로 묶어서 입력한다.
  • StackExchange, StackOverflow에 올라온 답변을 참고한다.
  • 직접 질문을 올리거나 버그 리포팅 시스템에 이슈를 등록한다.

Item 3. 선행 조건과 후행 조건 만족 여부 확인하기

  • 선행조건 (precondition), 후행조건(postcondition)

주의해아할 상황

  • 값이 null이 아니여야하는 부분이 null인지 살펴본다.
  • 수학 함수에 전달한 값이 그 함수에서 다루는 범위 안에 있는지 확인한다.(ex. log 함수에 전달한 값이 0보다 큰지 확인한다.)
  • 루틴에 전달한 객체나 구조체, 배열의 내부를 살펴보고 그 안에 필요한 내용이 담겨 있는지 확인한다.
  • 변순의 값이 정상 범위 안인지 확인한다. 간혹 변수를 초기화 하지 않으면 이상한 값이 들어 있는 경우가 있다.
  • 무작위 추출 검사(epot-check)를 통해 데이터 구조의 무결성을 검사한다.
  • 계산된 결과가 적합한지, 예상 범위 안에 있는지 확인한다.
  • 값이 정상 범위에 있다면 실제로 결과가 정확한지 확인한다. 직접 손으로 계산해보거나, 이미 알려진 정상 값과 비교해보거나, 다른 도구로 계산해보면 된다.
  • Side Effect가 예상된 결과인지, 의심 스러운 코드로 인해 데이터가 손상됬는지 엉뚱한 값으로 설정되어 있지 않은지 확인한다.
  • 알고리즘에서 사용하는 자원(File Handle, Lock 등)이 정상 반환 되었는지 확인한다.

Item 4. 문제 발생 지점부터 버그를 추적하거나 프로그램 시작 지점부터 버그를 찾아나가기

1. 프로그램이 갑자기 죽는 문제

  • 디버거로 실행해보거나 메모리 덤프를 보자
  • Magic Number를 확인하자
  • 변수 초기화 여부 확인(0xBAADF00D)

2. 프로그램이 멈춘 뒤 아무 반응이 없다면…

  • 원인을 추적하는 상향식 방식을 조금 다르게 진행한다. - 루프 바깥쪽부터 껍질식으로 탐색해나간다.

3. 에러 메시지가 명확할때

  • fgrep -r을 사용해서 복잡한 계층 구조 속에서 원하는 단어를 찾아내자.
  • 어느 코드에서 문제가 발생하는지 알 수 없을 때, 프로그램의 처음부터 추적하는 하향식 방식을 사용하는 것이 좋다. 이런 에러를 창발적 속성(에러) Emergent property라고 부른다.

기억해야할 사앙

  • 프로그램이 갑자기 종료하거나, 아무런 응답 없이 멈추거나, 에러 메시지를 출력될 떄와 같이 문제가 명확히 드러날 떄는 상향식 방식으로 원인을 찾는다.
  • 성능과 보안, 신뢰성 등과 같이 문제가 명확하게 드러나지 않을 때는 하향식 방식으로 원인을 찾는다.

Item 5. 정상 시스템과 비정상 시스템의 차이점 분석하기

  • 로그파일을 찬찬히 읽자.
  • 로그가 원하는 만큼 자세하지 않을 경우 Tracing Tool로 Runtime 을 분석해야한다.
    • DTrace, SystemTap을 사용(범용 도구)
    • strace, truss, Procmon(운영체제 호출과정 추척)
    • Itrace, Procmon (동적으로 링크된 라이브러리 호출과정)
    • tcpdump, Wireshark (네트워크 패킷 추적)
    • SQL 은 db 분석 도구를 사용한다.(흐음… 책에서 안알려준다. 찾아봐야겠다.)
    • R Project, 유닉스 어플리케이션은 보통 오류 찾기 힘들다. -x옵션으로 추적할수도 있지만… 눈물
  • 환경을 잘 구성하자
    • 최소한 파일 해쉬(MD5)값 정도는 확인하자. 흐음… binary file md5 값 보는 명령어 찾아봐야할듯
    • ldd를 이용하거나 dumpbin을 실행할 때 /dependents 옵션을 켜서 코드를 실행하는동안 동적 라이브러리도 함께 확인해보자.
    • 코드에서 사용하고 있는 기호(Symbol을 이렇게 번역한듯?)는 nm, Visual Studio는 dumpbin에서 /exports /imports 옵션을 켜서 확인하자
    • 자바라면 javap로 확인하자.
    • 환경변수는 간과하기 쉬운 요인이다.
    • git bisect 명령을 통해 버그가 나는 버전을 찾아내자.
    • cut 이나 awk 명령으로 쓸데 없는 부분은 잘라내자

기억할 사항

  • 정상 시스템과 오류가 발생한 시스템을 비교하여 문제의 원인을 찾는다.
  • 코드, 입력값, 함수를 호출할 때 전달한 인수, 환경변수, 서비스, 동적 라이브러리 등을 비롯한 시스템 동작에 영향을 미치는 요인을 모두 고려한다.

Item 6. 소프트웨어에서 제공하는 디버깅 기능 활용하기

  • 백그라운드 실행 또는 멀티스레드 기능을 꺼서 프로그램을 디버깅하기 쉽게 만들 수 있다.
  • 원하는 부분만 선택해서 실행함으로써 문제의 원인을 정확히 겨냥한 테스트 케이스를 작성할 수 있다.
  • 성능에 관련된 리포트나 고급 기능을 제공할 수 있다.
  • 부가적인 로깅 기능을 제공할 수 있다.

SQL에서 분석

  • explain키워드를 사용하자.

기억할 사항

  • 문제가 발생한 소프트웨어에서 자체적으로 디버깅 기능을 제공하는지 확인한다. 디버깅 기능을 제공하면 이를 활용하여 문제를 분석한다.

Item 7. 빌드 및 실행 환경을 다양하게 구성하기

환경 구성 방법

  1. 가상 머신 소프트웨어 사용
  2. 저렴한 소형 컴퓨터 (ex. raspberry pi)
  3. Cloud기반 호스트

컴파일러도 다양하게 구성하자

  • .NET Framework에서 개발할때 Mono도 함께 봐보자
  • Ada, C, C++, Objective C로 개발할때 LLVM과 GCC 를 함께 사용한다.
  • 자바에선 OpenJDK와 Oracle JDK, GNU Classpath를 함께 사용한다. 자바 런타임도 다양하게 하자
  • 루비 프로그램을 작성할 때 레퍼런스 구현인 CRuby와 별도로 JRuby, Rubinius, mruby와 같은 다른 VM 도 활용하자.

기억할 사항

  • 컴파일 환경과 실행 플랫폼을 다양하게 구성하면 다양한 시각으로 디버깅 작업을 수행할 수 있다.
  • 난해한 알고리즘에 관련한 문제는 고수준 언어로 구현하여 해결한다.

Item 8. 가장 중요한 문제에 집중하기

높은 우선순위

  • 데이터 손실
  • 보안
  • 서비스 가용성 저하
  • 안전
  • 충돌 또는 멈춤 현상
  • 코드 위생

낮은 우선순위

  • 레거시 지원
  • 하위 호환성
  • 미적인 이슈
  • 우회 방법 문서화
  • 거의 사용하지 않는 기능

기억할 사항

  • 모든 문제를 해결할 필요는 없다.
  • 우선순위가 낮은 이슈를 해결하는 작업은 우선순위가 높은 작업을 해결하는 데 필요한 시간을 빼앗을 수 있다.