가장 어려운 버그 찾기는 무엇이며 어떻게 찾아서 죽였습니까?


31

이것은 "지식 공유"질문입니다. 나는 당신의 성공 및 / 또는 실패로부터 배우고 싶습니다.

도움이 될만한 정보 ...

배경:

  • 상황 : 언어, 응용 프로그램, 환경 등
  • 버그는 어떻게 식별 되었습니까?
  • 누가 또는 무엇이 버그를 식별 했습니까?
  • 버그를 얼마나 복잡하게 재현 했습니까?

사냥.

  • 당신의 계획은 무엇입니까?
  • 어떤 어려움을 겪었습니까?
  • 문제의 코드는 어떻게 마침내 발견 되었습니까?

살해.

  • 수정은 얼마나 복잡 했습니까?
  • 수정 범위를 어떻게 결정 했습니까?
  • 수정에 얼마나 많은 코드가 관련되어 있습니까?

검시.

  • 근본적으로 근본 원인은 무엇입니까? 버퍼 오버런 등
  • 30,000 피트의 근본 원인은 무엇입니까?
  • 이 과정에 시간이 얼마나 걸렸습니까?
  • 수정 사항에 의해 부정적인 영향을받는 기능이 있습니까?
  • 어떤 방법, 도구, 동기가 특히 도움이 되었습니까? ... 끔찍하게 쓸모없는?
  • 다시 할 수 있다면? ............

이러한 예는 일반적이며 모든 상황에 적용 할 수있는 것은 아니며 쓸모가 없습니다. 필요에 따라 양념하십시오.

답변:


71

실제로 응용 프로그램의 타사 이미지 뷰어 하위 구성 요소에있었습니다.

우리는 응용 프로그램 사용자 중 2-3 명이 이미지 뷰어 구성 요소에서 예외를 발생시키고 끔찍하게 죽는다는 것을 알았습니다. 그러나 대부분의 업무 일 동안 동일한 작업에 응용 프로그램을 사용해도 문제를 본 적이없는 수십 명의 다른 사용자가있었습니다. 또한 특히 한 명의 사용자가 나머지 사용자보다 더 자주 사용했습니다.

우리는 일반적인 단계를 시도했습니다.

(1) 컴퓨터 / 구성을 배제하는 데 아무런 문제가 없었던 다른 사용자와 컴퓨터를 전환해야했습니다. -문제가 뒤따 랐습니다.

(2) 응용 프로그램에 로그인하여 문제를 본 적이없는 사용자로 작업했습니다. -문제는 여전히 뒤따 랐습니다.

(3) 사용자가보고있는 이미지를보고하고 그 이미지를 수천 번 빠르게 연속해서 볼 수 있도록 테스트 장치를 설정했습니다. 문제는 하네스에 나타나지 않았습니다.

(4) 개발자가 사용자와 함께 앉아서 하루 종일 그들을 보았습니다. 그들은 오류를 보았지만, 그것들을 유발시키기 위해 평범하지 않은 일을하는 것을 보지 못했습니다.

우리는 다른 사용자들이하지 않은 "오류 사용자"의 공통점을 파악하기 위해 몇 주 동안이 문제로 어려움을 겪었습니다. 어떻게해야할지 모르겠지만 (4) 단계의 개발자는 백과 사전 브라운에 합당한 어느 날 일을 시작하려고 유레카 순간을 보냈습니다.

그는 모든 "오류 사용자"가 왼손 잡임을 깨달았고이 사실을 확인했습니다. 왼손잡이 사용자 만 오류가 발생하지만 Righties는 절대 발생하지 않습니다. 그러나 어떻게 왼손잡이가 버그를 일으킬 수 있습니까?

우리는 그를 앉아서 왼손잡이가 다르게 행동하고있는 것에주의를 기울이는 것을 다시 보도록했고, 그것이 우리가 찾은 방법입니다.

새 이미지를로드하는 동안 이미지 뷰어에서 마우스를 픽셀의 가장 오른쪽 열 열로 이동 한 경우에만 버그가 발생하는 것으로 나타났습니다 (공급 업체가 mouseover 이벤트에 대해 일회성 계산을했기 때문에 오버플로 오류).

분명히 다음 이미지가로드되기를 기다리는 동안 사용자는 모두 자연스럽게 손 (그리고 마우스)을 키보드쪽으로 옮겼습니다.

가장 빈번하게 오류가 발생한 사용자는 다음 페이지가로드 될 때까지 마우스를 많이 움직여서 부지런히 움직 인 ADD 유형 중 하나 였으므로 마우스를 훨씬 더 빠르게 오른쪽으로 움직이고 로드 이벤트가 발생했을 때 타이밍을 측정했습니다. 우리가 공급 업체로부터 수정을 받기 전까지는 (다음 문서) 클릭 후 마우스를 놓아 두었다가로드 될 때까지 만지지 말라고 지시했습니다.

그 이후로 개발팀의 전설에서 "왼손잡이 버그" 로 알려졌습니다.


14
그것은 내가 들어 본 것 중 가장 악한 것입니다.
Nathan Taylor

9
그래도 그것을 해결 한 사람에게서 영웅을 만들었습니다.
JohnFx

2
와우, 지금 그것은 버그의 지옥입니다!
Mitchel Sellers

3
좋은 발견! 좋은 이야기.
Toon Krijthe

11
마치 우리 좌파들이 이미 2 등 시민처럼 취급되지 않는 것처럼. 이제 우리는 또한 소프트웨어 버그에 대한 공정한 점유율보다 더 많이 싸워야합니다 ... 고마워요! : p
Dan Molding

11

이것은 오래 전 (1980 년대 후반)입니다.

제가 근무했던 회사는 다양한 유닉스 워크 스테이션 (HP, Sun, Silcon Graphics 등)에서 실행되는 CAD 패키지 (FORTRAN)를 작성했습니다. 우리는 데이터를 저장하기 위해 자체 파일 형식을 사용했으며 패키지가 시작될 때 디스크 공간이 부족하여 엔터티 헤더에 여러 플래그를 저장하는 데 많은 비트 이동이 사용되었습니다.

엔티티의 유형 (선, 호, 텍스트 등)을 저장할 때 4096 (생각합니다)을 곱했습니다. 또한이 값은 삭제 된 항목을 나타 내기 위해 부정되었습니다. 따라서 유형을 얻으려면 다음과 같은 코드를 사용하십시오.

type = record[1] MOD 4096

하나를 제외한 모든 기계에서 이것은 ± 1 (선의 경우), ± 2 (호의 경우) 등을 제공했으며 서명이 삭제되었는지 확인할 수 있습니다.

한 대의 기계 (HP는 생각합니다)에서 삭제 된 항목 처리가 망가지는 이상한 문제가있었습니다.

이것은 IDE와 비주얼 디버거 이전의 시절에 발생했기 때문에 추적 문과 로깅을 삽입하여 문제를 추적하고 추적해야했습니다.

나는 결국 다른 모든 제조업체가 구현 중에 있기 때문에 것을 발견 MOD너무 -4096 MOD 4096결과 -1HP 수학적으로 정확하게 그래서 그것을 구현 -4096 MOD 4096결과 -4097.

결국 전체 코드베이스를 거쳐 값의 부호를 저장하고 양수로 만들기 전에 MOD결과를 부호 값으로 곱해야했습니다.

며칠이 걸렸습니다.


3
아마도 몇 년 동안 더 어려운 버그 찾기가 있었을 것입니다. 그러나 이것은 20 년 넘게 제 생각에 떠 오릅니다!
ChrisF

7

와, 잘 읽어보세요!

터보 파스칼이 커졌을 때 몇 년 전 이었지만, 그 당시의 초기 C ++ IDE 중 하나 였을 수도 있습니다. 독창적 인 개발자 (이 스타트 업에서 세 번째 사람)로서 저는 영업 담당자에게 친숙한 CAD 프로그램과 같은 것을 작성했습니다. 당시에는 훌륭했지만 무작위로 추락했습니다. 재현 할 수는 없지만 버그 찾기를 시작하기에 충분히 자주 발생했습니다.

최선의 전략은 디버거에서 한 단계 만 거치는 것이 었습니다. 버그는 사용자가 그림을 충분히 입력하고 특정 모드 또는 확대 / 축소 상태에있을 때만 발생하므로 지루한 설정과 중단 점 제거가 많았으며 1 분 동안 정상적으로 그림을 입력 한 다음 그림을 입력했습니다. 큰 코드 덩어리를 밟습니다. 조정 가능한 횟수만큼 건너 뛰고 중단되는 중단 점이 특히 도움이되었습니다. 이 전체 운동을 여러 번 반복해야했습니다.

결국 나는 그것을 서브 루틴이 호출되는 곳으로 좁히고 2를 얻었지만 그 안에서 약간의 횡설수설을 보았습니다. 나는 이것을 더 일찍 붙잡을 수 있었지만 그것이 주어진 것을 얻었다 고 가정 하면서이 서브 루틴으로 들어 가지 않았습니다. 가장 단순한 것이 좋다고 가정하여 눈을 멀게했습니다!

스택에 16 비트 int를 채우는 것으로 나타 났지만 서브 루틴은 32 비트를 기대합니다. 아니면 그런 것. 컴파일러가 모든 값을 32 비트로 자동으로 채우지 않았거나 충분한 유형 검사를 수행하지 않았습니다. 한 줄의 일부만 고치는 것은 사소한 일이지만, 필요한 생각은 거의 없습니다. 그러나 그곳에 가기 위해서는 3 일 동안 사냥을하고 명백한 질문을했습니다.

그래서 나는 고가의 컨설턴트가 들어온 것에 대한 일화에 대한 개인적인 경험을 가지고 있으며, 잠시 후 한 번의 탭으로 2000 달러를 청구합니다. 경영진은 고장을 요구하며 탭은 1 달러, 탭할 위치는 1999 달러입니다. 내 경우를 제외하고는 시간이 돈이 아니었다.

교훈 : 1) 최고의 컴파일러를 사용하십시오. 여기서 "최상의"는 컴퓨터 과학이 확인하는 방법을 알고있는만큼 많은 문제를 확인하는 것과 2) 간단한 명백한 사항에 대한 질문을하거나 최소한 적절한 기능을 검증하는 것을 포함합니다.

그 이후로 필요한 것보다 더 간단한 것을 점검하는 것을 알고 있기 때문에 모든 어려운 버그는 정말 어려웠습니다.

레슨 2는 내가 수정 한 가장 어려운 전자 버그와 사소한 수정에도 적용되지만 여러 스마트 EE가 몇 달 동안 중단되었습니다. 그러나 이것은 전자 포럼이 아니므로 더 이상 말하지 않겠습니다.


다른 곳에 전자 버그를 게시하고 여기에 링크를 게시하십시오!
tgkprog

6

지옥에서 네트워킹 데이터 경쟁 조건

다른 개발자가 작성한 실제로 오래된 (Encore 32/77) 워크 스테이션에서 유사한 응용 프로그램과 작동하도록 네트워킹 클라이언트 / 서버 (Windows XP / C #)를 작성하고있었습니다.

응용 프로그램은 기본적으로 PC의 다중 모니터 터치 스크린 UI로 시스템을 실행하는 호스트 프로세스를 제어하기 위해 호스트의 특정 데이터를 공유 / 조작했습니다.

3 계층 구조 로이 작업을 수행했습니다. 통신 프로세스는 호스트와의 데이터 읽기 / 쓰기, 필요한 모든 형식 변환 (엔디안, 부동 소수점 형식 등)을 수행하고 데이터베이스에서 값을 쓰거나 읽었습니다. 데이터베이스는 통신 및 터치 스크린 UI 간의 데이터 중개 역할을했습니다. 터치 스크린 UI의 앱은 PC에 연결된 모니터 수에 따라 터치 스크린 인터페이스를 생성했습니다 (자동 감지).

주어진 시간 프레임에서 호스트와 PC 사이의 값 패킷은 왕복 당 ~ 110ms의 최대 대기 시간으로 한 번에 최대 128 개의 값을 전송할 수 있습니다 (UDP는 직접 x-over 이더넷 연결과 함께 사용되었습니다 컴퓨터). 따라서 연결된 터치 스크린의 변수 수에 따라 허용되는 변수 수는 엄격하게 제어됩니다. 또한 호스트 (실시간 컴퓨팅에 사용되는 공유 메모리 버스가있는 매우 복잡한 다중 프로세서 아키텍처를 가지고 있음에도 불구하고)는 휴대 전화의 처리 능력의 약 1/100을 가졌으므로 가능한 한 적은 처리를 수행해야하며 서버입니다. / client는 이것을 보장하기 위해 어셈블리로 작성해야했습니다 (호스트는 프로그램에 영향을받지 않는 전체 실시간 시뮬레이션을 실행하고있었습니다).

문제였습니다. 터치 스크린에서 변경 될 때 일부 값은 새로 입력 한 값만 가져 가지 않고 해당 값과 이전 값 사이에서 무작위로 순환합니다. 특정 페이지 조합이있는 특정 페이지의 일부 특정 값에서만 증상이 나타납니다. 초기 고객 수락 프로세스를 통해 문제를 시작하기 전까지는 문제를 거의 놓치지 않았습니다.


문제를 해결하기 위해 진동 값 중 하나를 선택했습니다.

  • 터치 스크린 앱을 확인했는데 진동하고있었습니다
  • 데이터베이스를 확인하고 진동
  • 통신 앱을 확인하고 진동

그런 다음 wireshark를 중단하고 패킷 캡처를 수동으로 디코딩하기 시작했습니다. 결과:

  • 진동하지 않지만 패킷이 제대로 보이지 않아 너무 많은 데이터가있었습니다.

결함 / 오류가없는 것을 발견하면서 통신 코드의 모든 세부 사항을 수백 번 밟았습니다.

마지막으로 나는 다른 개발자에게 이메일이 없어지기 시작했습니다. 그런 다음 찾았습니다.

분명히, 그는 데이터를 전송할 때 전송하기 전에 데이터 배열을 플러시하지 않았으므로 본질적으로 이전 값을 덮어 쓰는 새 값과 함께 사용 된 마지막 버퍼를 덮어 쓰지만 덮어 쓰지 않은 이전 값은 여전히 ​​전송됩니다.

따라서 값이 데이터 배열의 위치 80에 있고 요청 된 값 목록이 80 미만으로 변경되었지만 동일한 값이 새 목록에 포함 된 경우 두 값 모두 해당 특정 버퍼의 데이터 버퍼에 존재합니다. 주어진 시간.

데이터베이스에서 읽는 값은 UI가 값을 요청한 시간 조각에 따라 다릅니다.


수정은 매우 간단했습니다. 데이터 버퍼에 들어오는 항목 수 (실제로는 패킷 프로토콜의 일부로 포함됨)를 읽고 해당 항목 수를 초과하여 버퍼를 읽지 마십시오.


교훈:

  • 현대 컴퓨팅 능력을 당연한 것으로 여기지 마십시오. 컴퓨터가 이더넷을 지원하지 않았고 어레이를 플러시 할 때 비용이 많이들 수 있습니다. 우리가 얼마나 멀리 왔는지보고 싶다면, 동적 메모리 할당 형태가 거의없는 시스템을 상상해보십시오. IE의 경영진 프로세스는 모든 프로그램의 모든 메모리를 순서대로 사전 할당해야했으며, 그 범위를 넘어서는 프로그램은 성장할 수 없었습니다. IE는 전체 시스템을 다시 컴파일하지 않고 프로그램에 더 많은 메모리를 할당하면 막대한 충돌이 발생할 수 있습니다. 사람들이 언젠가 같은 시각에 쓰레기 수거 전날에 대해 이야기할지 궁금합니다.

  • 사용자 정의 프로토콜로 네트워킹을 수행하거나 일반적으로 이진 데이터 표현을 처리 할 때 파이프를 통해 전송되는 모든 값의 모든 기능을 이해할 때까지 사양을 읽으십시오. 내 눈이 아플 때까지 읽어봐 사람들은 개별 비트 또는 바이트를 조작하여 데이터를 처리하며 매우 영리하고 효율적인 방식으로 작업을 수행합니다. 아주 작은 디테일이 없으면 시스템이 손상 될 수 있습니다.

문제를 해결하는 데 걸리는 시간은 대부분 2 ~ 3 일이며 대부분의 시간은 내가 이것에 좌절했을 때 다른 일을하는 데 소비했습니다.

참고 : 해당 호스트 컴퓨터는 기본적으로 이더넷을 지원하지 않았습니다. 이 카드를 구동하는 카드는 맞춤형으로 개조되었으며 프로토콜 스택은 사실상 존재하지 않았습니다. 내가 함께 일한 개발자는 프로그래머의 지옥이었다.이 프로젝트를 위해 시스템에서 제거 된 UDP 버전과 최소 가짜 이더넷 스택 (프로세서가 전체 이더넷 스택을 처리 할만 큼 강력하지는 않음)을 구현했을뿐만 아니라 그러나 그는 일주일도 채 걸리지 않았습니다. 그는 처음에 OS를 설계하고 프로그래밍 한 최초의 프로젝트 팀 리더 중 한 사람이었습니다. 컴퓨터 / 프로그래밍 / 아키텍처에 관해 그가 얼마나 오래 감아 왔는지, 또는 이미 얼마나 새로운 지에 상관없이 그가 말해야 할 것은 무엇이든 말입니다.


5

배경

  • 웹 사이트를 운영하고 백엔드 trasactional 처리를 제공하는 미션 크리티컬 WCF 애플리케이션 ..
  • 대용량 애플리케이션 (초당 수백 건의 통화)
  • 다중 서버 다중 인스턴스
  • 수백 건의 통과 된 단위 테스트 및 수많은 QA 공격

버그

  • 프로덕션으로 이동하면 서버가 임의의 시간 동안 정상적으로 실행 된 후 빠르게 저하되기 시작하고 박스 CPU를 100 %로 가져갑니다.

내가 찾은 방법

처음에는 이것이 정상적인 성능 문제라고 확신하여 정교한 로깅을 작성했습니다. 사용률에 대해 데이터베이스 사람들과 대화 할 때마다 모든 호출의 성능을 확인하여 서버의 문제를 감시했습니다. 일주

그런 다음 스레드 경합 문제가 있다고 확신했습니다. 교착 상태가 디버그에서 상황을 작성하기 위해 상황 작성 도구를 작성하려고 시도한 것을 확인했습니다. 관리 좌절감이 커짐에 따라 동료들에게 프로젝트를 처음부터 다시 시작하는 것부터 서버를 하나의 스레드로 제한하는 방법을 제안했습니다. 1.5 주

그런 다음 Tess Ferrandez 블로그에서 사용자 덤프 파일을 작성하고 다음에 서버가 덤프를 가져 왔을 때 windebug로이를 정리했습니다. 내 모든 스레드가 dictionary.add 함수에 붙어 있음을 발견했습니다.

x 스레드 오류를 기록하는 로그를 추적 한 짧은 하나의 작은 사전이 동기화되지 않았습니다.


3

하드웨어 장치와 통신하는 응용 프로그램이 있는데, 경우에 따라 장치를 다시 연결하고 두 번 소프트 리셋 할 때까지 물리적으로 플러그를 뽑으면 제대로 작동하지 않을 수 있습니다.

문제는 시작시 실행중인 응용 프로그램이 아직 마운트되지 않은 파일 시스템 (예 : 사용자가 NFS 볼륨에서 읽도록 구성한 경우)에서 읽으려고 할 때 가끔 segfaulting하는 것으로 나타났습니다. 시작할 때 응용 프로그램은 일부 ioctl을 드라이버로 보내 장치를 초기화 한 다음 구성 설정을 읽고 더 많은 ioctl을 보내 장치를 올바른 상태로 만듭니다.

드라이버의 버그로 인해 초기화 호출을 수행 할 때 유효하지 않은 값이 장치에 기록되었지만 장치가 특정 상태가되도록 호출 한 후에는 유효한 데이터로 값을 덮어 썼습니다.

장치 자체에는 배터리가 있으며 마더 보드에서 전원이 끊겼는지 감지하고 휘발성 메모리에 플래그를 써서 전원이 끊 겼음을 나타내며 다음에 전원을 켰을 때 특정 상태로 들어갑니다. 깃발을 지우려면 명령을 보내야했습니다.

문제는 일단 ioctl이 장치를 초기화하기 위해 전송되고 유효하지 않은 값을 장치에 기록한 후 유효한 데이터가 전송되기 전에 전원이 제거 된 경우입니다. 장치의 전원을 다시 켤 때 플래그가 설정되었음을 확인하고 불완전한 초기화로 인해 드라이버에서 전송 된 유효하지 않은 데이터를 읽으려고합니다. 이렇게하면 전원 끄기 플래그가 지워진 장치가 유효하지 않은 상태가되지만 장치가 드라이버에 의해 다시 초기화 될 때까지 추가 명령을받지 못합니다. 두 번째 재설정은 디바이스가 디바이스에 저장된 유효하지 않은 데이터를 읽으려고하지 않았으며 올바른 구성 지시 사항을 수신하여 올바른 상태로 놓을 수 있음을 의미합니다 (ioctl을 전송하는 애플리케이션이 segfault가 아니라고 가정 함) ).

결국 문제를 일으킨 정확한 상황을 파악하는 데 약 2 주가 걸렸습니다.


2

University 프로젝트의 경우 파일을 공유하는 분산 P2P 노드 시스템을 작성했습니다.이 멀티 캐스팅은 서로를 감지하고 여러 노드의 링과 네임 서버를 감지하여 노드가 클라이언트에 할당되었습니다.

C ++로 작성 되었으므로 POCO 를 사용하여 멋진 IO, 소켓 및 스레드 프로그래밍이 가능합니다.


우리를 괴롭 히고 많은 시간을 잃게 만드는 두 가지 버그가있었습니다. 정말 논리입니다.

무작위로 컴퓨터가 원격 IP 대신 로컬 호스트 IP를 공유하고있었습니다.

이로 인해 클라이언트는 동일한 PC의 노드에 연결되거나 노드가 자신과 연결됩니다.

우리는 이것을 어떻게 식별 했습니까? 네임 서버의 출력을 개선 할 때 우리는 나중에 컴퓨터가 컴퓨터를 재부팅했을 때 제공 할 IP를 결정하는 스크립트가 잘못되었음을 발견했습니다. 무작위로 lo 장치가 eth0 장치 대신에 처음으로 나열되었습니다. 이제 우리는 모든 대학 컴퓨터에서 공유되므로 eth0에서 요청하도록 하드 코딩했습니다 ...


그리고 지금 더 성가신 것 :

무작위로 패킷 흐름이 임의로 일시 중지됩니다.
다음 클라이언트가 연결되면 계속됩니다 ...

이것은 실제로 무작위로 발생했으며 두 대 이상의 컴퓨터가 관련되어 있기 때문에이 문제를 디버깅하는 것이 더 성가시다. 대학 컴퓨터는 우리가 Wireshark를 실행할 수 없으므로 문제가 송신 측인지 수신 측인지 추측 할 수 있습니다. 측면.

코드에서 많은 출력을 통해 우리는 명령을 보내는 것이 잘 진행된다고 가정했습니다
. 실제 문제가 어디에 있는지 궁금해했습니다 .POCO 폴링 방식이 잘못되었고 대신 사용 가능한 문자를 확인해야합니다. 들어오는 소켓에.

우리는 이것이 적은 패킷을 포함하는 프로토 타입에서 더 간단한 테스트로 작동한다고 가정 하여이 문제를 일으키지 않았기 때문에 설문 조사가 작동한다고 가정했지만 ... 그렇지 않았습니다. :-(


교훈:

  • 네트워크 장치의 순서와 같은 어리석은 가정을하지 마십시오.

  • 프레임 워크가 항상 직무 (구현 또는 문서)를하는 것은 아닙니다.

  • 코드에 충분한 출력을 제공하십시오. 허용되지 않는 경우 확장 된 세부 사항을 파일에 기록하십시오.

  • 코드가 단위 테스트되지 않은 경우 (너무 어렵 기 때문에) 작동하는 것으로 가정하지 마십시오.


1
wireshark (또는 유사한 도구)없이 네트워킹 문제를 해결하는 것은 iteslf에서 영웅적입니다.
Evan Plaice

2

나는 여전히 가장 어려운 버그 찾기 중입니다. 그것은 때때로 거기에 있고 버그가 아닌 사람들 중 하나입니다. 그래서 다음 날 오전 6시 10 분에 여기에 있습니다.

배경:

  • 상황 : 언어, 응용 프로그램, 환경 등
    • PHP OS 커머스
  • 버그는 어떻게 식별 되었습니까?
    • 무작위 순서는 무작위로 실패하고 경로 재 지정 문제로 작동합니다.
  • 누가 또는 무엇이 버그를 식별 했습니까?
    • 클라이언트 및 리디렉션 문제가 분명했습니다.
  • 버그를 얼마나 복잡하게 재현 했습니까?
    • 나는 재현 할 수 없었지만 클라이언트는 할 수 있었다.

사냥.

  • 당신의 계획은 무엇입니까?
    • 디버그 코드 추가, 주문 채우기, 데이터 분석, 반복
  • 어떤 어려움을 겪었습니까?
    • 반복 가능한 문제와 끔찍한 코드 부족
  • 문제의 코드는 어떻게 마침내 발견 되었습니까?
    • 불쾌한 코드가 많이 발견되었습니다. 정확히 내가 정확히 필요한 것은 아닙니다.

살해.

  • 수정은 얼마나 복잡 했습니까?
    • 대단히
  • 수정 범위를 어떻게 결정 했습니까?
    • 범위가 없었습니다 ... 그것은 어디에나있었습니다.
  • 수정에 얼마나 많은 코드가 관련되어 있습니까?
    • 그것의 모든? 파일을 건드리지 않았다고 생각하지 않습니다

검시.

  • 근본적으로 근본 원인은 무엇입니까? 버퍼 오버런 등
    • 나쁜 코딩 연습
  • 30,000 피트의 근본 원인은 무엇입니까?
    • 나는 말하지 않을 것입니다 ...
  • 이 과정에 시간이 얼마나 걸렸습니까?
    • 영원히 그리고 하루
  • 수정 사항에 의해 부정적인 영향을받는 기능이 있습니까?
    • 특색? 아니면 버그입니까?
  • 어떤 방법, 도구, 동기가 특히 도움이 되었습니까? ... 끔찍하게 쓸모없는?
  • 다시 할 수 있다면? ............
    • ctrl + a 델

이유가 "나쁜 코딩 관행"이라면 팀의 코딩 관행을 수정하고 동료 검토를 도입하기에 좋은시기인지 상사와상의하고 싶습니까?

2

나는 마지막 semseter를 혼란스럽게하는 몇 가지 문제를 해결해야했지만 여전히 나에게 가장 눈에 띄는 버그는 숙제 할당을 위해 PDP-11 어셈블리에서 작성하는 텍스트 기반 게임에있었습니다. 그것은 Conway의 Game of Life를 기반으로했으며 이상한 이유로 그리드 옆에있는 정보의 많은 부분이 지속적으로 존재해서는 안되는 정보로 덮어 쓰여졌습니다. 논리도 매우 간단했기 때문에 매우 혼란 스러웠습니다. 모든 논리가 올바른지 다시 발견하기 위해 여러 번 진행 한 후에 갑자기 문제가 무엇인지 알았습니다. 이건 :.

PDP-11에서 숫자 옆에있는이 작은 점은 8 대신 10을 기준으로합니다. 크기는 같은 숫자로 정의되었지만 기본에는 격자로 제한되어야하는 루프를 묶은 숫자 옆에있었습니다. 8.

작은 4 픽셀 크기의 추가로 인해 피해가 많기 때문에 여전히 나에게 눈에.니다. 결론은 무엇입니까? PDP-11 어셈블리에서 코딩하지 마십시오.


2

메인 프레임 프로그램이 이유없이 작동을 멈췄습니다

방금 이것을 다른 질문에 게시했습니다. 여기에 게시물보기

메인 프레임에 최신 버전의 컴파일러를 설치했기 때문에 발생했습니다.

06/11/13 업데이트 : (Original answer가 OP에 의해 삭제되었습니다.)

이 메인 프레임 응용 프로그램을 상속했습니다 . 어느 날, 청록색에서 작동이 멈췄습니다. 그게 다야 .. 멈 췄어.

내 직업은 가능한 빨리 작동시키는 것이 었습니다. 소스 코드는 2 년 동안 수정되지 않았지만 갑자기 중단되었습니다. 코드를 컴파일하려고했는데 XX 줄에서 끊어졌습니다. XX 행을 보았는데 XX 행을 중단시킬 항목을 알 수 없었습니다. 이 응용 프로그램에 대한 자세한 사양을 요청했지만 아무것도 없었습니다. XX 행은 범인이 아니 었습니다.

코드를 인쇄하여 위에서 아래로 검토하기 시작했습니다. 나는 무슨 일이 일어나고 있는지에 대한 순서도를 만들기 시작했다. 코드는 너무 복잡해서 이해가 거의되지 않았습니다. 나는 순서도를 포기하려고 포기했다. 특히 응용 프로그램이 수행 한 작업에 대한 세부 정보가 없기 때문에 해당 변경 사항이 프로세스의 나머지 부분에 어떤 영향을 미치는지 모른 채 변경하기를 두려워했습니다.

그래서 소스 코드 맨 위에서 시작하여 코드를 더 읽기 쉽게하기 위해 whitespce와 line 브레이크를 추가하기로 결정했습니다. 어떤 경우에는 AND와 OR을 결합한 조건이 있었으며 어떤 데이터가 AND되고 있고 어떤 데이터가 OR되고 있는지를 명확하게 구분할 수 없었습니다. 그래서 AND 및 OR 조건을 괄호로 묶어 더 읽기 쉽도록했습니다.

청소를 천천히 진행하면서 정기적으로 작업을 저장했습니다. 어느 시점에서 나는 코드를 컴파일하려고 시도했고 이상한 일이 일어났다. 오류가 코드의 원래 줄을 넘어서서 더 이상 내려갔습니다. 그래서 AND와 OR 조건을 parens로 분리하면서 계속했습니다. 청소가 끝나면 효과가있었습니다. 그림을 이동.

그런 다음 운영 상점을 방문하여 최근에 메인 프레임에 새로운 구성 요소를 설치했는지 물어보기로 결정했습니다. 예, 우리는 최근에 컴파일러를 업그레이드했습니다. 흠.

이전 컴파일러는 관계없이 왼쪽에서 오른쪽으로 식을 평가했습니다. 새로운 버전의 컴파일러는 왼쪽에서 오른쪽으로 식을 평가했지만 AND와 OR의 불명확 한 조합을 확인할 수없는 모호한 코드를 확인할 수있었습니다.

내가 배운 교훈은 ... 항상, 항상, 서로 결합하여 사용될 때 항상 조건을 분리하기 위해 parens를 사용합니다.


링크가 가리키는 게시물이 삭제되었습니다. 답변을 업데이트 하시겠습니까?
gnat

1
@gnat-archive.org에서 찾았습니다 :)
Michael Riley-AKA Gunny

1

배경:

  • 컨텍스트 : 고객이 직접 체크인 할 수있는 웹 서버 (C ++)
  • 버그 : 페이지를 요청할 때 페이지가 응답하지 않고 전체 팜이 있으며 프로세스가 페이지를 처리하는 데 너무 오래 걸리기 때문에 (몇 초만 허용됨) 프로세스가 종료 (재시작)됩니다.
  • 일부 사용자는 불만을 표명했지만 매우 산발적이어서 대부분 눈에 띄지 않았습니다 (페이지를 제공하지 않으면 사람들이 "새로 고침"을 누르는 경향이 있습니다). 우리는 핵심 덤프를 보았습니다.)
  • 우리는 실제로 로컬 환경에서 재현 할 수 없었습니다. 버그는 테스트 시스템에 몇 번 나타 났지만 성능 테스트 중에는 나타나지 않았습니다.

사냥.

  • 계획 : 음, 메모리 덤프와 로그가 있었으므로 분석하고 싶었습니다. 그것이 전체 팜에 영향을 미쳤고 과거에 데이터베이스 문제가 있었기 때문에 데이터베이스를 의심했습니다 (여러 서버의 단일 DB)
  • 난이도 : 전체 서버 덤프는 매우 커서 공간이 부족하지 않은 경우가 많으므로 자주 지워 지므로 문제가 발생했을 때 신속하게 파악해야했습니다. 덤프는 다양한 스택을 보여 주었고 (DB를 많이 사용하지는 않음) 페이지 자체를 준비하는 동안 실패했으며 (이전 계산에서는 아님) 로그가 표시 한 내용을 확인하여 페이지를 준비하는 데 때로는 오랜 시간이 걸릴 수도 있습니다. 사전 계산 된 데이터가있는 기본 템플릿 엔진 일 뿐이지 만 (전통적인 MVC)
  • 시작하기 : 더 많은 샘플과 약간의 생각 후에 HDD (페이지 템플릿)에서 데이터를 읽는 데 시간이 걸린다는 것을 깨달았습니다. 전체 팜에 관한 것이기 때문에 우리는 처음에 예정된 작업 (크론 탭, 배치)을 찾았지만 타이밍이 한 사건에서 다른 사건으로 결코 일치하지 않았습니다 ... 마침내 새 버전이 활성화 되기 며칠 전에 항상 발생했습니다. 소프트웨어와 나는 AhAh했다! 순간 ... 소프트웨어의 배포로 인한 것입니다! 수백 메가 바이트 (압축 된)를 제공하면 디스크 성능이 약간 저하 될 수 있습니다. 물론 배포가 자동화되고 아카이브가 모든 서버에 한 번에 푸시됩니다 (멀티 캐스트).

살해.

  • 복잡성 수정 : 컴파일 된 템플릿으로 전환
  • 영향을받는 코드 : 없음, 빌드 프로세스의 간단한 변경

검시.

  • 근본 원인 : 운영 문제 또는 사전 계획 부족 :)
  • 타임 스케일 : 추적, 수정 및 테스트에 며칠, QA 및 성능 테스트 및 배포에 몇 주가 소요되었습니다. 수정을 배포하면 버그가 발생한다는 것을 알았으므로 서두르지 않아도됩니다. 그렇지 않으면 ... 정말 변태!
  • 부작용 : 런타임에 템플릿을 제공된 코드로 작성하여 템플릿으로 전환 할 수 없음 일반적으로 템플릿을 전환하면 더 많은 데이터를 쏟을 수 있기 때문에이 기능은 많이 사용하지 않았습니다. "작은"레이아웃 변경에 충분합니다.
  • 방법, 도구 : gdb+ 모니터링! 디스크를 의심 한 다음 모니터링 그래프에서 활동 스파이크의 원인을 식별하는 데 시간이 걸렸습니다 ...
  • 다음에 : 모든 IO를 부작용으로 취급하십시오!

1

가장 어려운 것은 공장 운영으로 전체 생산 환경 이외의 다른 곳에서는 재현 할 수 없기 때문에 결코 죽지 않았습니다.

내가 죽인 가장 미친 것 :

그림이 횡설수설하고 있습니다!

코드를보고 아무것도 볼 수 없습니다. 프린터 대기열에서 작업을 꺼내서 검사하면 제대로 보입니다. (이것은 HPGl / 2가 내장 된 PCL5 시대에 실제로 사용되었습니다. 실제로 도면을 플롯하는 데 매우 적합하며 제한된 메모리에 래스터 이미지를 작성하는 데 어려움이 없습니다.) 그것을 이해해야하는 다른 프린터로 보냅니다. .

코드를 롤백하면 여전히 문제가 있습니다.

마지막으로 수동으로 간단한 파일을 만들어 프린터로 보냅니다. 프린터 자체가 아니라 내 버그가 아니라는 것이 밝혀졌습니다. 유지 보수 회사는 다른 것을 고칠 때 최신 버전으로 플래시하고 최신 버전에 버그가있었습니다. 그들이 중요한 기능을 수행하고 이전 버전으로 다시 플래시해야한다는 것을 이해하게하는 것은 버그 자체를 찾는 것보다 어렵습니다.

훨씬 더 독창적이지만 내 상자에만 있었기 때문에 처음에는 넣지 않았습니다.

지원되지 않는 일부 API를 처리하기위한 DPMI 코드 인 Borland Pascal 때때로 작동하지만 때로는 잘못된 포인터를 처리하려고 시도했습니다. 그러나 포인터를 밟을 것으로 예상되는 것처럼 잘못된 결과를 얻지 못했습니다.

디버그-코드를 한 단계 씩 실행하면 항상 올바르게 작동하지만 그렇지 않으면 이전과 마찬가지로 불안정합니다. 검사는 항상 올바른 값을 보여주었습니다.

범인 : 두 사람이있었습니다.

1) Borland의 라이브러리 코드에는 중대한 버그가있었습니다. 실제 모드 포인터는 보호 모드에서 포인터 변수에 저장되었습니다. 문제는 대부분의 실제 모드 포인터가 보호 모드에서 유효하지 않은 세그먼트 주소를 가지고 있으며 포인터를 복사하려고 할 때 포인터 쌍을 레지스터 쌍에로드 한 다음 저장했습니다.

2) 디버거는 단일 단계 모드에서 이러한 잘못된로드에 대해 아무 말도하지 않습니다. 내부적으로 무엇을했는지 모르지만 사용자에게 제시된 내용은 완전히 올바른 것으로 보입니다. 실제로 명령을 실행하지 않고 대신 시뮬레이션하는 것으로 의심됩니다.


1

이것은 어떻게 든 내가 악몽으로 변한 매우 간단한 버그입니다.

배경 : 나는 나만의 운영체제를 만들기 위해 노력하고있었습니다. 디버깅은 매우 어렵습니다 (추적 문 만 있으면됩니다)

버그 : 사용자 모드에서 두 개의 스레드 스위치를 수행하는 대신 일반 보호 오류가 발생합니다.

버그 찾기 : 아마이 문제를 해결하기 위해 1-2 주가 걸렸습니다. 어디에나 추적 문 삽입 생성 된 어셈블리 코드 검사 (GCC에서). 내가 할 수있는 모든 가치를 인쇄합니다.

문제 : 버그 사냥 초기에 hltcrt0에 명령을 넣었습니다. crt0은 기본적으로 운영 체제에서 사용하기 위해 사용자 프로그램을 부트 스트랩합니다. 이hlt 명령은 사용자 모드에서 실행될 때 GPF를 발생시킵니다. 나는 그것을 거기에 놓고 기본적으로 잊어 버렸습니다. (원래 문제는 버퍼 오버플로 또는 메모리 할당 오류였습니다.)

수정 : hlt지침을 제거하십시오 :) 그것을 제거한 후 모든 것이 원활하게 작동했습니다.

내가 배운 것 : 문제를 디버깅하려고 할 때 시도한 수정 사항을 놓치지 마십시오. 안정적인 최신 소스 제어 버전을 정기적으로 비교하고 다른 것이 없을 때 최근에 변경 한 내용을 확인하십시오.

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