소프트웨어로 문제 / 문제 / 버그를 디버깅하려고 할 때 일반적으로 어떤 프로세스를 사용합니까? [닫은]


15

대부분의 사람들은 디버깅이 과학이 아닌 예술로 취급하는 것 같습니다. 예술이 아닌 과학으로 여기있는 사람들에게는 새로운 이슈 / 버그 / 문제에 직면했을 때 일반적으로 어떤 프로세스를 사용합니까?

답변:


13

매우 일반적인 용어로, 내가하는 일은 :

  1. 문제를 격리 시키십시오. 버그가 처음 나타 났을 때 무엇이 ​​바뀌 었는지 생각해보십시오. 어디에서 일하고 있습니까? 코드의 어떤 부분을 변경 했습니까? 내 버그의 99 %가이 방법으로 해결됩니다. 일반적으로 어리석은 일입니다.

  2. 문제의 위치를 ​​추측하면 원인으로 보이는 코드를 잘 살펴보십시오. 읽어. 큰소리로 읽습니다. "무엇을 달성하려고합니까?" 일부 유형의 문제의 경우 : 부작용이 있거나 다른 곳에서는 생각하지 못한 방식으로 코드의 영향을받을 수 있습니까?

  3. 언제 어디서 잘못되었는지 분석하기 위해 다양한 방법으로 시도하십시오 (아래 참조).

  4. 여전히 실마리가 없다면, 이전 버전의 소스에 동일한 문제가 있는지 확인하고 개발 타임 라인에서 문제가 처음 나타난시기를 찾아보십시오. 이를 위해서는 git과 같은 좋은 버전 제어 시스템으로 작업해야합니다 (git에는 정확하게 이런 종류의 디버깅을 위해 bisect라는 기능이 있습니다).

  5. 여전히 실마리가 없다면 휴식을 취하십시오. 실제로 도움이됩니다.

  6. 드로잉 보드로 돌아가서 프로그램의 작동 방식과 실제로 의미가 있는지 검토하십시오.

그것은 실제로 문제의 종류에 달려 있지만 문제의 위치에 대한 일반적인 아이디어가 있다고 가정하면 다음과 같습니다.

  • 문제가 코드 / 최근 변경의 일부에 있다고 생각되면 먼저 코드를 더 간단하게 만들어 버그를 제거하거나 주석 처리 / 변경하거나 문제를 해결하려고 시도하고 문제가있는 코드를 다시 가져 와서 잘보세요.

  • 중단 점이있는 디버거를 실행하고 (가능한 경우) 데이터가 잘못 작동하기 시작하는 시점을 찾아내어 데이터가 어떻게 보이는지 더 잘 파악하는 방법을 살펴보십시오.


1
휴식을 취하기 위해 +1 가장 어려운 문제는 좌절하고 6 시간 안에 디버깅 할 때 더욱 어려워집니다. 휴식 시간을 아는 것은 내가 얻은 가장 유용한 디버깅 기술 중 하나입니다.
Brad Gardner

멋진 답변입니다. 더 잘할 수 없습니다.
EricBoersma

1
내 접근 방식과 매우 유사하지만 동료에게 신속하게 살펴 보라고 요청하고 즉시 철자 실수를 발견하는 부분을 잊어 버렸습니다 ...
ChrisAnnODell

1
훌륭한 답변입니다. 예방의 온스가 1 파운드의 치료 가치가 있다고 덧붙이고 싶습니다. 디버깅 프로세스의 큰 부분은 처음에 코딩하는 동안 작고 점진적으로 변경하고 각 프로세스 사이에서 로컬로 컴파일, 테스트 및 커밋하는 것입니다. 이렇게하면 버그가 갑자기 나타나면 예상되는 의심 목록이 매우 작아서 bzr qdiff명령으로 쉽게 볼 수 있습니다.
Karl Bielefeldt

8

테스트 중심 개발 ( TDD ) 을 사용하려고합니다 . 버그를 복제하는 테스트를 작성한 다음 테스트를 통과하려고합니다. 때때로 테스트 작성은 버그를 찾는 데 도움이됩니다.

이로 인해 대부분의 경우 디버거에서 벗어날 수 있으며 버그가 다시 발생하지 않도록 회귀 테스트를 제공합니다.

일부 링크 :


4
이 답변은 매우 불완전하다고 생각합니다. 나는 많은 공감대를 이해하지 못한다.
Alex

1
그것은 마법의 약어 TDD를 포함하기 때문에 많은 찬사를 얻습니다.
Bjarke Freund-Hansen

@Alex-링크를 추가했습니다. "버그 찾기, 테스트 쓰기"예제가 있습니다. 나는 이것을 확장 할 수 있지만 실제로는 간단합니다.
TrueWill

7

과학이라는 단어에는 여러 가지 정의가 있지만 " 과학적 방법 " 이라고하는 것을 더 정확하게 언급하고있는 것 같습니다 . 과학적 방법은 일부 현상 (아마도 버그 또는 예상치 못한 프로그램 동작)을 관찰하고, 동작을 설명하기 위해 가설 또는 가설을 공식화하고,이를 입증하기 위해 실험 할 가능성이 가장 높습니다 (문제를 안정적으로 재현하는 테스트 작성).

발생할 수있는 버그 유형 (현상)은 사실상 무한하며 일부는 반드시 잘 정의 된 프로세스를 요구하지는 않습니다. 예를 들어, 때때로 버그를 관찰하면 코드에 매우 익숙하기 때문에 버그의 원인을 즉시 알 수 있습니다. 다른 경우에는 일부 입력 (작업, 일련의 단계 등)이 주어지면 잘못된 결과 (충돌, 잘못된 출력 등)가 발생한다는 것을 알고 있습니다. 그러한 경우에는 종종 "과학적인"사고가 많이 필요하지 않습니다. 일부 생각은 검색 공간을 줄이는 데 도움이 될 수 있지만 일반적인 방법은 디버거에서 코드를 단계별로 살펴보고 문제가 발생한 부분을 확인하는 것입니다.

그러나 가장 흥미롭고 과학적 프로세스에 합당하다고 생각되는 상황은 최종 결과가 나 왔으며 어떻게 발생했는지 설명해 달라는 요청입니다. 이것의 명백한 예는 크래시 덤프입니다. 크래시 덤프를로드하고 시스템 상태를 관찰 할 수 있으며 해당 상태에서 어떻게 작동하는지 설명해야합니다. 크래시 (또는 코어) 덤프는 예외, 교착 상태, 내부 오류 또는 사용자가 정의한 일부 "원치 않는"상태 (예 : 부진)를 표시 할 수 있습니다. 이러한 상황에서는 일반적으로 다음과 같은 단계를 따릅니다.

  • 좁은 관찰 : 해당되는 경우 특정 문제를 직접 둘러싼 정보를 연구하십시오. 여기서 명백한 것은 호출 스택, 로컬 변수, 볼 수있는 경우 로컬 변수, 문제를 둘러싼 코드 줄입니다. 이러한 유형의 특정 위치 연구는 항상 적용 가능한 것은 아닙니다. 예를 들어, "느린"시스템을 연구 할 때 이와 같이 시작 위치가 명확하지 않을 수 있지만 충돌 또는 내부 오류 상황에 즉각적이고 명백한 관심 지점이있을 수 있습니다. 여기서 한 가지 구체적인 단계는 windbg와 같은 도구를 사용하는 것입니다 (로드 된 크래시 덤프에서! analyze -v를 실행하고 알려주는 내용을보십시오).

  • 넓은 관측 : 시스템의 다른 부분을 연구합니다. 시스템의 모든 스레드 상태를 검사하고 모든 글로벌 정보 (사용자 / 작업 / 항목 수, 활성 트랜잭션 / 프로세스 / 위젯 등), 시스템 (OS) 정보 등을 확인하십시오. 사용자가 외부 세부 사항을 제공 한 경우 관찰 한 것과 관련하여 생각하십시오. 예를 들어, 매주 화요일 오후에 문제가 발생한다고 말하면 그것이 무엇을 의미하는지 스스로에게 물어보십시오.

  • 가설: 이것은 정말로 재미있는 부분입니다 (그리고 나는 그것이 재미 있다는 것에 신경 쓰지 않습니다). 종종 많은 논리적 사고가 반대로 요구됩니다. 시스템이 현재 상태에 어떻게 도달했는지 생각하는 것은 매우 즐거울 수 있습니다. 나는 이것이 많은 사람들이 예술이라고 생각하는 부분이라고 생각합니다. 그리고 프로그래머가 막 붙이는 것을보기 위해 무작위로 물건을 던지기 시작했을 수도 있습니다. 그러나 경험이 있다면 이것은 상당히 명확한 과정 일 수 있습니다. 이 시점에서 매우 논리적으로 생각하면 주어진 상태로 이어지는 가능한 경로 세트를 정의하는 것이 종종 가능합니다. 나는 우리가 상태 S5에 있다는 것을 알고 있습니다. 이를 위해서는 S4a 또는 S4b가 발생해야하고 S4a 이전의 S3 등이 필요할 수 있습니다. 그렇지 않은 경우에는 종종 주어진 상태로 이어질 수있는 여러 항목이있을 수 있습니다. 때로는 간단한 흐름 또는 상태 다이어그램 또는 일련의 시간 관련 단계를 스크래치 패드에 기록하는 것이 도움이 될 수 있습니다. 여기서 실제 프로세스는 상황에 따라 크게 다르지만이 시점에서 진지한 생각 (및 이전 단계에서 다시 검사)을 수행하면 종종 하나 이상의 그럴듯한 답변이 제공됩니다. 또한이 단계에서 매우 중요한 부분은 불가능한 것을 제거하는 것입니다. 불가능을 제거하면 솔루션 공간을 정리하는 데 도움이됩니다 (Sherlock Holmes가 불가능을 제거한 후 남은 것에 대해 말한 것을 기억하십시오). 또한이 단계에서 매우 중요한 부분은 불가능한 것을 제거하는 것입니다. 불가능을 제거하면 솔루션 공간을 정리하는 데 도움이됩니다 (Sherlock Holmes가 불가능을 제거한 후 남은 것에 대해 말한 것을 기억하십시오). 또한이 단계에서 매우 중요한 부분은 불가능한 것을 제거하는 것입니다. 불가능을 제거하면 솔루션 공간을 정리하는 데 도움이됩니다 (Sherlock Holmes가 불가능을 제거한 후 남은 것에 대해 말한 것을 기억하십시오).

  • 실험 :이 단계에서는 이전 단계에서 도출 된 가설을 바탕으로 문제를 재현 해 봅니다. 이전 단계에서 진지한 생각을했다면 이것은 매우 간단해야합니다. 때때로 나는 주어진 테스트를 돕기 위해 코드베이스를 "속임수"로 수정합니다. 예를 들어, 최근 경쟁 조건에서 발생한 충돌을 조사하고있었습니다. 이를 확인하기 위해 다른 스레드가 "적절한"시간에 다른 작업을 수행 할 수 있도록 몇 줄의 코드 사이에 Sleep (500)을 추가하기 만하면됩니다. 이것이 "실제"과학에서 허용되는지는 모르겠지만 자신이 소유 한 코드에서는 완벽하게 합리적입니다.

당신이 그것을 재현하는 데 성공한다면, 당신은 거의 다 끝났을 것입니다 (남은 것은 그것을 고치는 간단한 단계입니다 ...하지만 다른 날입니다). 새로운 테스트를 회귀 테스트 시스템으로 점검하십시오. 그리고 나는 그것을 간단하게 수정하는 것에 대한 이전의 진술이 혀로 혀지는 것을 의도했음을 지적해야한다. 솔루션을 찾아서 구현하려면 광범위한 작업이 필요할 수 있습니다. 버그 수정은 디버깅 프로세스의 일부가 아니라 개발이라고 생각합니다. 그리고 수정 프로그램이 포함 된 경우 어느 정도의 디자인과 검토가 필요합니다.


내가 본 대부분의 버그는 안정적으로 재현 할 수 없었으며, 하위 세트의 경우 대부분은 버그를 수정하기위한 작업을 시작하기 전에 재현 한 후에도 상당한 디버깅 작업이 필요했습니다. "복제에 성공했다"고 말하는 대신 "버그를 명확하게 수행하는 단위 테스트를 좁히는 데 성공했다"고 말하지만 디버깅 작업이 끝나지 않았다고 말할 수 있습니다. 나에게 디버깅이 끝나면 수정이 끝났으며 문제를 해결할 수 있음을 증명할 수 있으며 내 수정이 실제로 문제를 해결한다는 확실한 증거가 있습니다.
blueberryfields

나는 그것을 고치는 것이 많은 일이 될 수 있다는 데 동의합니다. 나는 실제로 "고정의 간단한 단계"라는 말로 풍자를 사용하고 있었지만, 그 유형은 잘 나오지 않았다.

4

테스트 사례를 줄이십시오. 크기가 작 으면 일반적으로 문제를 일으키는 해당 코드를 찾는 것이 더 쉽습니다.

새로운 체크인으로 인해 문제가 발생했을 수 있으며 이전 일일 빌드는 문제가 없었습니다. 이 경우 소스 컨트롤의 변경 로그를 통해 파악할 사람을 결정하는 데 도움이됩니다.

또한 C / C ++를 사용하는 경우 valgrind를 실행하거나 메모리 관련 문제를 격리하기 위해 정화하십시오.


2

디버깅의 가장 어려운 부분은 문제가 여러 계층 아래에 ​​묻혀있을 때 특히 문제를 격리시키는 것입니다. 대학에서 나는 음악 녹음을 공부했고, 이상하게도 여기에 직접 적용되는 Studio Electronics 수업이있었습니다. 스튜디오 환경 디버깅을 체계적인 디버깅 프로세스의 예시로 사용하겠습니다.

  1. 미터를 테스트하십시오. 알려진 보정 된 전압에서 테스트 톤을 사용하면 미터에 "U"(유니티 게인)가 표시되어야합니다. 번역 : 도구가 파손 된 경우 다른 도구를 사용하여 무엇이 잘못되었는지 파악할 수 없습니다.
  2. 각 구성 요소 / 게인 단계는 끝에서 거꾸로 작동합니다. 스테이지의 입력에 적용되는 것과 동일한 테스트 톤을 사용하면 스테이지의 출력에 변화가 없어야합니다. 번역 : 우리가 어질러 자리를 찾을 때까지 우리는 우리의 코드에 대한 신뢰를 구축하고 출력 뒤쪽에서 각 개체를 분리함으로써. 도구가 문제를 알리는 데 몇 개의 레이어가 필요한 경우 사이에있는 레이어가 문제에 영향을 미치지 않는다는 것을 알아야합니다.

코드 디버깅은 그렇게 다르지 않습니다. 코드에서 예외가 발생하면 디버깅이 훨씬 쉽습니다. 해당 예외의 스택 추적에서 뒤로 추적하고 주요 위치에서 중단 점을 설정할 수 있습니다. 일반적으로 변수를 설정 한 직후 또는 예외를 발생시키는 메소드를 호출하는 행에서. 하나 이상의 값이 올바르지 않다는 것을 알 수 있습니다. 그것이 맞지 않으면 (값이 없어야하거나 값이 범위를 벗어날 때 null 인 경우), 왜 올바르지 않은지를 발견하는 과정입니다. IDE의 브레이크 포인트는 전자 테스트 포인트 (미터의 프로브가 회로를 확인하도록 설계됨)와 같습니다.

이제, 실제 문제가 어디 있는지 발견하는 데 어려움을 겪은 후에는 나중에이를 확인하기 위해 몇 가지 단위 테스트를 작성합니다.


2

오후 늦게 추적하기 위해 고생하는 벌레와 함께, 가장 효과적인 전략은 일어 서서 몇 분 동안 걸어가는 것입니다. 일반적으로 가능한 오류 원인에 대한 새로운 아이디어는 30 초 후에 시작됩니다.


2

보다 실용적인 접근 방법 :

  1. 버그가 처리되지 않은 예외와 관련이있는 경우 스택 추적을보십시오. Null 참조, 범위를 벗어난 색인 및 자신이 정의한 예외가 가장 흔합니다.이 버그를 주니어 개발자에게 할당 할 수 있습니다. 아마도 쉽고 좋은 학습 경험입니다.

  2. 모든 머신에서 발생하지 않는다면 아마도 일종의 경쟁 조건 / 스레딩 문제 일 것입니다. 이들은 지루한 수석 프로그래머를 추적하는 것이 매우 재미 있습니다. 많은 로깅, 좋은 지식 및 훌륭한 도구가이 작업을 수행합니다.

  3. 또 다른 큰 종류의 버그는 테스트 팀이나 고객이 특정 행동을 좋아하지 않을 때입니다. 예를 들어 사용자 ID를 표시하기로 결정하거나 검색 할 때 자동 완성되지 않는 것을 좋아하지 않습니다. 이것들은 진짜 버그이며, 더 나은 제품 관리와 더 넓은 시야를 가진 개발자를 고려하십시오. 확장을 염두에두고 시스템을 구축하는 경우 개발자가이 문제를 "수정"하는 데 비교적 짧은 시간이 걸립니다.

  4. 다른 모든 버그의 80 %는 훌륭한 로깅 시스템을 갖추고 충분한 정보를 수집하여 해결합니다. Log4Net / Log4J와 같은 여러 수준의 복잡한 로깅 시스템과 함께 빌트인 추적 사용

  5. 퍼포먼스 버그는 그 자체의 범주이며, 여기서 가장 중요한 규칙은 "먼저 측정하고, 나중에 수정하십시오!"이며, 얼마나 많은 개발자가 문제의 위치를 ​​추측하고 문제를 해결하기 위해 바로 들어가는지를보고 놀랄 것입니다. 나중에 응답 시간이 3-4 % 감소했습니다.


이 5 개 중 하나를 개별적으로 +1 할 수 있다면 그렇게 할 것입니다!
jmort253

1

흐름 두 가지 접근 방식이 있습니다.

  1. 주어진 문제를 작은 부분으로 Divide and Conquer나눈 다음 패러다임에 따라 작은 부분을 각각 정복하십시오 .
  2. 내가 어떤 값에 관해 의문이 생길 때마다 변수의 값을 출력하여 정확히 무엇이 들어오고 나가는 지 볼 수 있습니다.

이 접근법은 대부분의 시간에 도움이되었습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.