지금까지 발견하고 수정 한 가장 어려운 버그는 무엇입니까?
무엇을 찾기가 어려웠습니까? 어떻게 추적 했습니까?
닫을만큼 가깝지는 않지만 https://stackoverflow.com/questions/175854/what-is-the-funniest-bug-youve-ever-experienced 도
참조하십시오.
감시 카메라에서 실행되는 jpeg 파서는 회사의 CEO가 방에 들어올 때마다 충돌했습니다.
100 % 재현 가능한 오류.
농담이 아니야!
이는 이유:
JPEG 압축에 대해 잘 모르는 분들을 위해 이미지는 일종의 작은 블록 매트릭스로 분할 된 다음 마술 등을 사용하여 인코딩됩니다.
파서는 CEO가 방에 들어 왔을 때 목이 막혔습니다. 왜냐하면 그는 항상 사각형 패턴이있는 셔츠를 가지고 있었기 때문에 콘트라스트와 블록 경계 알고리즘의 특별한 경우를 유발했습니다.
정말 클래식합니다.
이것은 나에게 일어나지 않았지만 친구가 그것에 대해 말했습니다.
그는 거의 충돌하지 않는 앱을 디버깅해야했습니다. 9 월 9 일 이후 수요일에만 실패했습니다. 예, 1 년 중 362 일이면 괜찮 았고 1 년 중 3 일 동안 즉시 충돌이 발생했습니다.
날짜 형식을 "Wednesday, September 22 2008"로 지정하지만 버퍼가 한 문자가 너무 짧습니다. 따라서 해당 월에 가장 긴 이름을 가진 날에 2 자리 DOM이있는 경우에만 문제가 발생합니다. 가장 긴 이름.
이를 위해서는 Z-8000 어셈블러에 대한 지식이 필요하며, 이에 대해 설명하겠습니다.
저는 임베디드 시스템 (Z-8000 어셈블러)에서 작업하고있었습니다. 회사의 다른 부서는 동일한 플랫폼에 다른 시스템을 구축하고 함수 라이브러리를 작성했으며이 라이브러리를 프로젝트에서도 사용했습니다. 버그는 내가 하나의 함수를 호출 할 때마다 프로그램이 충돌한다는 것입니다. 모든 입력을 확인했습니다. 그들은 괜찮 았습니다. 라이브러리가 전국에있는 수천 개의 POS 사이트에서 사용되었고 제대로 작동하고 있다는 점을 제외하면 라이브러리의 버그 여야했습니다.
이제 Z-8000 CPU에는 16 개의 16 비트 레지스터 R0, R1, R2 ... R15가 있으며, RR0, RR2, RR4..RR14 등의 8 개의 32 비트 레지스터로 주소를 지정할 수도 있습니다. 라이브러리가 작성되었습니다. 처음부터 오래된 라이브러리를 리팩토링합니다. 매우 깨끗하고 엄격한 프로그래밍 표준을 따랐습니다. 각 함수가 시작될 때 함수에 사용되는 모든 레지스터는 값을 유지하기 위해 스택으로 푸시되었습니다. 모든 것이 깔끔하고 깔끔했습니다. 완벽했습니다.
그럼에도 불구하고 라이브러리에 대한 어셈블러 목록을 연구 한 결과 해당 함수에 대해 이상한 점이 있음을 발견했습니다. --- 함수 시작시에는 PUSH RR0 / PUSH RR2가 있었고 마지막에는 POP RR2 / POP R0이있었습니다. 이제 그것을 따르지 않으면 처음에 스택에 4 개의 값을 푸시했지만 끝에는 3 개만 제거했습니다. 그것은 재앙의 비결입니다. 반환 주소가 있어야하는 스택 상단에 알 수없는 값이 있습니다. 기능이 작동하지 않을 수 있습니다.
제가 상기시켜 드릴 수있는 점을 제외하고는 작동했습니다. 수천 대의 컴퓨터에서 하루에 수천 번 호출되었습니다. 작동하지 않을 수도 있습니다.
얼마 동안 디버깅 (1980 년대 중반의 도구를 사용하는 임베디드 시스템의 어셈블러에서는 쉽지 않음) 후에는 잘못된 값이 임의의 주소로 전송했기 때문에 반환시 항상 충돌이 발생했습니다. 분명히 나는 그것이 실패하지 않은 이유를 파악하기 위해 작동하는 앱을 디버깅해야했다.
음, 라이브러리는 레지스터의 값을 보존하는 데 매우 훌륭 했으므로 레지스터에 값을 입력하면 그대로 유지됩니다. R1에는 0000이 들어 있습니다. 해당 함수가 호출 될 때 항상 0000이 있습니다. 따라서 버그는 스택에 0000을 남겼습니다. 따라서 함수가 반환되면 주소 0000으로 점프합니다.이 주소는 RET가되어 스택에서 다음 값 (올바른 반환 주소)을 꺼내서 해당 주소로 점프합니다. 데이터는 버그를 완벽하게가 렸습니다.
물론 내 앱에서는 R1에서 다른 값을 가졌기 때문에 그냥 충돌했습니다 ....
이것은 Linux에서 발생했지만 거의 모든 OS에서 발생할 수 있습니다. 이제 여러분 대부분은 BSD 소켓 API에 익숙 할 것입니다. 우리는 해마다 그것을 행복하게 사용하고 있으며 작동합니다.
우리는 많은 소켓이 열려있는 대규모 병렬 애플리케이션을 개발하고있었습니다. 작동을 테스트하기 위해 데이터 전송을 위해 수백 개, 때로는 수천 개 이상의 연결을 여는 테스트 팀이있었습니다. 채널 번호가 가장 높으면 응용 프로그램이 이상한 동작을 보이기 시작합니다. 때때로 그것은 단지 추락했습니다. 또 다른 경우에는 단순히 참일 수없는 오류가 발생했습니다 (예 : accept ()가 후속 호출에서 동일한 파일 설명자를 반환하여 당연히 혼란을 초래했습니다.)
로그 파일에서 무언가 잘못되었다는 것을 알 수 있었지만 정확히 파악하기가 엄청나게 어려웠습니다. Rational Purify를 사용한 테스트는 잘못된 것이 없다고 말했습니다. 그러나 뭔가 잘못되었습니다. 우리는 며칠 동안이 작업을했고 점점 좌절감을 느꼈습니다. 이미 협상 된 테스트가 앱에 혼란을 야기 할 것이기 때문에 그것은 쇼 블로커였습니다.
오류는 고부하 상황에서만 발생했기 때문에 소켓으로 수행 한 모든 작업을 다시 확인했습니다. 이러한 메모리 집약적 상황에서 실행 가능하지 않았기 때문에 Purify에서 고부하 사례를 테스트 한 적이 없습니다.
마지막으로 (그리고 운 좋게도) 소켓의 상태 변경을 기다리는 select ()에 엄청난 수의 소켓이 문제가 될 수 있다는 것을 기억했습니다 (읽기 / 쓰기 / 오류 가능). 확실히 우리의 애플리케이션은 디스크립터 1025로 소켓에 도달 한 순간에 혼란을 일으키기 시작했습니다. 문제는 select ()가 비트 필드 매개 변수와 함께 작동한다는 것입니다. 비트 필드는 매크로 FD_SET () 및 해당 매개 변수의 유효성을 확인하지 않는 친구로 채워집니다.
따라서 1024 개 이상의 설명자를 얻을 때마다 (각 OS에는 자체 제한이 있고 Linux 바닐라 커널에는 1024가 있으며 실제 값은 FD_SETSIZE로 정의 됨) FD_SET 매크로는 기꺼이 비트 필드를 덮어 쓰고 메모리의 다음 구조에 가비지를 기록합니다.
모든 select () 호출을 신비한 select () 호출에 대한 잘 설계된 대안 인 poll ()으로 대체했으며, 높은로드 상황은 그 이후로 문제가되지 않았습니다. 모든 소켓 처리가 15 분의 작업으로 문제를 해결할 수있는 하나의 프레임 워크 클래스에 있었기 때문에 운이 좋았습니다. select () 호출이 코드 전체에 뿌려 졌다면 훨씬 더 나쁠 것입니다.
교훈:
API 함수가 25 년이되어 모두가 사용하더라도 아직 모르는 어두운 구석이있을 수 있습니다.
API 매크로에서 확인되지 않은 메모리 쓰기는 EVIL입니다.
Purify와 같은 디버깅 도구는 모든 상황, 특히 많은 메모리가 사용되는 경우에 도움이되지 않습니다.
가능하면 항상 애플리케이션에 대한 프레임 워크를 확보하십시오. 이를 사용하면 이식성이 향상 될뿐만 아니라 API 버그 발생시에도 도움이됩니다.
많은 응용 프로그램은 소켓 제한에 대해 생각하지 않고 select ()를 사용합니다. 그래서 나는 단순히 많은 소켓을 사용함으로써 많은 인기있는 소프트웨어에서 버그를 일으킬 수 있다고 확신합니다. 고맙게도 대부분의 응용 프로그램에는 1024 개 이상의 소켓이 없습니다.
안전한 API를 갖는 대신 OS 개발자는 개발자를 비난하는 것을 좋아합니다. Linux select () 매뉴얼 페이지에
"설명자 값이 0보다 작거나 FD_SETSIZE보다 크거나 같으면 이러한 매크로의 동작은 정의되지 않습니다. 이는 일반적으로 시스템에서 지원하는 최대 설명자 수와 동일합니다."
오해의 소지가 있습니다. Linux는 1024 개 이상의 소켓을 열 수 있습니다. 그리고 동작은 절대적으로 잘 정의되어 있습니다. 예기치 않은 값을 사용하면 실행중인 응용 프로그램이 망가집니다. 매크로를 잘못된 값에 복원하는 대신 개발자는 단순히 다른 구조를 덮어 씁니다. FD_SET은 Linux 헤더에서 인라인 어셈블리 (!)로 구현되며 단일 어셈블러 쓰기 명령어로 평가됩니다. 어느 곳에서나 일어나는 사소한 경계 검사가 아닙니다.
자신의 응용 프로그램을 테스트하려면 main () 바로 뒤에 FD_SETSIZE 파일 또는 소켓을 프로그래밍 방식으로 연 다음 응용 프로그램을 실행하여 사용되는 설명자 수를 인위적으로 늘릴 수 있습니다.
Thorsten79
내 하드웨어 문제 였어 ...
예전에는 대형 21 인치 CRT 모니터가있는 DEC VaxStation을 사용했습니다. 우리는 새 건물의 실험실로 이동하여 방의 반대쪽 구석에 두 개의 VaxStation을 설치했습니다. 전원을 켜자 모니터가 디스코처럼 깜박 거 렸습니다. (예, 80 년대였습니다) 그러나 다른 모니터는 그렇지 않았습니다.
좋아, 모니터를 바꾼다. 다른 모니터 (지금은 내 VaxStation에 연결됨)가 깜박이고 이전 모니터 (방을 가로 질러 이동)는 깜박이지 않았습니다.
나는 CRT 기반 모니터가 자기장에 취약하다는 것을 기억했습니다. 사실, 그들은 60Hz 교류 자기장에 매우 민감했습니다. 나는 즉시 내 작업 영역에서 60Hz의 자기장을 생성하는 것으로 의심했습니다.
처음에는 작업 영역에서 무언가를 의심했습니다. 안타깝게도 다른 모든 장비를 끄고 플러그를 뽑았을 때도 모니터가 여전히 깜박입니다. 그 시점에서 나는 건물에서 무언가를 의심하기 시작했습니다.
이 이론을 테스트하기 위해 VaxStation과 85lb 모니터를 휴대용 시스템으로 전환했습니다. 우리는 전체 시스템을 롤 어라운드 카트에 놓고 100 피트 주황색 건설 연장 코드에 연결했습니다. 계획은 문제가되는 장비를 찾기 위해이 설정을 휴대용 전계 강도 측정기로 사용하는 것이 었습니다.
모니터를 돌리는 것은 우리를 완전히 혼란스럽게했습니다. 모니터는 방의 정확히 절반에서 깜빡 였지만 다른 쪽에서는 깜빡이지 않았습니다. 방은 정사각형 모양이고 반대쪽 모서리에 문이 있고 모니터는 문을 연결하는 진단 선의 한쪽에서 깜박 거 렸지만 다른 쪽은 아닙니다. 방은 복도로 사방이 둘러싸여 있었다. 모니터를 복도로 밀어 내자 깜박임이 멈췄습니다. 사실, 우리는 플리커가 방의 삼각형 모양의 절반에서만 발생하고 다른 곳에서는 발생하지 않는다는 것을 발견했습니다.
완전히 혼란 스러웠던 후, 나는 방에 각 문에 조명 스위치가있는 양방향 천장 조명 시스템이 있다는 것을 기억했습니다. 그 순간 나는 무엇이 잘못되었는지 깨달았다.
나는 문제가있는 방의 절반으로 모니터를 옮기고 천장 조명을 껐다. 깜박임이 멈췄습니다. 조명을 켰을 때 깜박임이 다시 시작되었습니다. 조명 스위치 중 하나에서 조명을 켜거나 끄고 방의 절반 내에서 깜박임을 켜거나 끕니다.
문제는 천장 조명을 연결할 때 누군가가 모서리를 깎아서 발생했습니다. 조명 회로에서 양방향 스위치를 배선 할 때 SPDT 스위치 접점 사이에 한 쌍의 전선을 연결하고 한 스위치의 공통에서 단일 전선을 조명을 통해 다른 스위치의 공통으로 연결합니다.
일반적으로 이러한 전선은 함께 묶여 있습니다. 그들은 하나의 스위치 박스에서 그룹으로 떠나 천장 천장 고정 장치로 달린 다음 다른 상자로 실행됩니다. 핵심 아이디어는 모든 전류 전달 전선이 함께 묶여 있다는 것입니다.
건물이 배선되었을 때 스위치와 조명 사이의 단일 와이어는 천장을 통해 라우팅되었지만 스위치 사이를 이동하는 와이어는 벽을 통해 라우팅되었습니다.
모든 와이어가 서로 가깝고 평행하게 연결된 경우 한 와이어의 전류에 의해 생성 된 자기장은 근처 와이어의 동일 및 반대 전류에 의해 생성 된 자기장에 의해 상쇄됩니다. 불행히도 조명이 실제로 배선 된 방식은 방의 절반이 기본적으로 대형 단일 턴 트랜스포머 1 차 내부에 있음을 의미했습니다. 조명이 켜지면 전류가 루프로 흐르고 불량한 모니터는 기본적으로 대형 전자석 내부에 놓여있었습니다.
이야기의 도덕 : AC 전원 배선의 핫 라인과 중립 라인은 합당한 이유로 서로 옆에 있습니다.
이제 제가해야 할 일은 경영진에게 새 건물의 일부를 다시 배선해야하는 이유를 설명하는 것뿐이었습니다 ...
일부 코드를 발견하고이를 연구 한 후 "이렇게 작동 할 수있는 방법은 없습니다!"라는 결론을 내립니다. 이전에는 항상 작동했지만 갑자기 작동이 중지됩니다.
내 작업에서 구축하는 데 도움을 준 제품 중 하나는 고객 사이트에서 몇 달 동안 실행되어 수신 된 각 이벤트를 수집하고 즐겁게 기록하여 SQL Server 데이터베이스에 기록했습니다. 약 6 개월 동안 매우 잘 실행되어 약 3 천 5 백만 레코드를 수집했습니다.
그러던 어느 날 고객이 데이터베이스가 거의 2 주 동안 업데이트되지 않은 이유를 물었습니다. 추가 조사에서 삽입을 수행하는 데이터베이스 연결이 ODBC 호출에서 반환되지 않는 것으로 나타났습니다. 고맙게도 녹음을 수행하는 스레드가 나머지 스레드와 분리되어 녹음 스레드를 제외한 모든 것이 거의 2 주 동안 올바르게 작동 할 수있었습니다!
우리는이 기계가 아닌 다른 기계에서 문제를 재현하기 위해 몇 주 동안 노력했습니다. 우리는 문제를 재현 할 수 없습니다. 안타깝게도 다른 여러 제품이 거의 동일한 방식으로 실패하기 시작했습니다. 데이터베이스 스레드가 나머지 기능과 분리되어 있지 않아 전체 애플리케이션이 중단되고 매번 수동으로 다시 시작해야했습니다. 추락했습니다.
몇 주에 걸친 조사가 몇 달로 바뀌었지만 데이터베이스를 사용한 모든 응용 프로그램에서 전체 ODBC 교착 상태라는 동일한 증상이 계속 발생했습니다. 이때까지 우리 제품은 디버깅 정보와 문제가 무엇인지, 어디서 잘못되었는지 확인하는 방법으로 가득 차 있습니다. 심지어 일부 제품이 교착 상태를 감지하고 정보를 수집하고 결과를 이메일로 보낸 다음 다시 시작하는 시점까지도 마찬가지입니다.
어느 날 서버에서 작업하는 동안 애플리케이션이 충돌했을 때 디버깅 정보를 수집하면서 무슨 일이 일어나고 있는지 알아 내려고 노력했습니다. 서버 BSoD가 저에게 있습니다. 서버가 다시 온라인 상태가되었을 때 WinDbg에서 미니 덤프를 열어 문제가되는 드라이버가 무엇인지 알아 냈습니다. 파일 이름을 가져 와서 실제 파일을 추적했습니다. 파일의 버전 정보를 살펴본 결과, 컴퓨터에 설치된 McAfee 안티 바이러스 제품군의 일부임을 알았습니다.
우리는 안티 바이러스를 비활성화했고 그 이후로 단 한 가지 문제도 없었습니다 !!
이 Google 영역에서 발생할 수있는 매우 흔하고 불쾌한 버그 인 코드 붙여 넣기 및 악명 높은 마이너스 를 지적하고 싶습니다.
일반 ASCII 문자 하이픈 빼기 ( '-') 대신 마이너스가 있는 일부 코드 를 복사하여 붙여 넣을 때 입니다.
더하기, 빼기 (U + 2212), 하이픈 빼기 (U + 002D)
이제 마이너스가 하이픈 마이너스보다 길게 렌더링되었다고 가정하지만 특정 편집기 (또는 DOS 쉘 창)에서는 사용 된 문자 집합에 따라 실제로 일반 '-'하이픈 마이너스 기호로 렌더링됩니다.
그리고 ...이 코드가 컴파일되지 않는 이유를 파악하고 실제 원인을 찾을 때까지 각 줄을 하나씩 제거하는 데 몇 시간을 소비 할 수 있습니다!
가장 어려운 버그는 아니지만 충분히 실망 스럽습니다.)
( 내 원래 게시물에서 반전을 발견 한 ShreevatsaR 에게 감사드립니다 -댓글 참조)
첫 번째는 출시 된 제품에 버그가 있었지만 디버깅을 시도했을 때 문제가 발생하지 않았습니다. 처음에는 이것이 "릴리스 vs. 디버그"라고 생각했지만 릴리스 모드에서 코드를 컴파일해도 문제를 재현 할 수 없었습니다. 다른 개발자가 문제를 재현 할 수 있는지 확인했습니다. 아니. 프로그램 출력을 많이 조사 (혼합 어셈블리 코드 / C 코드 목록 생성)하고 출시 된 제품의 어셈블리 코드를 단계별로 살펴본 후 (yuck!) 문제가되는 줄을 발견했습니다. 그러나 선은 나에게 잘 보였다! 그런 다음 어셈블리 명령이 무엇을했는지 찾아야했고 릴리스 된 실행 파일에 잘못된 어셈블리 명령이 있는지 충분히 확인해야했습니다. 그런 다음 빌드 환경에서 생성 한 실행 파일을 확인했습니다. 올바른 어셈블리 명령이 있습니다. 빌드 머신이 어떻게 든 손상되어이 응용 프로그램에 대한 하나의 명령에 대해서만 잘못된 어셈블리 코드를 생성 한 것으로 나타났습니다. 다른 모든 것 (우리 제품의 이전 버전 포함)은 다른 개발자 컴퓨터와 동일한 코드를 생성했습니다. 소프트웨어 관리자에게 연구 결과를 보여준 후 우리는 빌드 머신을 빠르게 재 구축했습니다.
네트워크 애플리케이션의 내부 깊숙한 곳에는 다음과 같은 라인이 있습니다 (단순화 됨).
if (socket = accept() == 0)
return false;
//code using the socket()
호출이 성공하면 어떻게 되었습니까? socket
1로 설정되었습니다. send()
1이 주어지면 무엇을 합니까? (예 :
send(socket, "mystring", 7);
그것은 stdout
...로 인쇄됩니다 . 이는 4 시간 후에 발견했습니다. 왜 내 모든 것을 printf()
꺼냈을 때 내 앱이 네트워크를 통해 데이터를 보내는 대신 터미널 창에 인쇄하고 있는지 궁금해했습니다 .
80 년대에 Data General 미니 컴퓨터에서 FORTRAN을 사용하면 컴파일러가 상수 1 (1)을 0 (영)으로 처리하도록하는 경우가있었습니다. 일부 오래된 코드가 변수를 FORTRAN 매개 변수로 선언 한 함수에 값 1의 상수를 전달했기 때문에 발생했습니다. 즉, 변경 불가능한 것으로 간주됩니다. 코드의 결함으로 인해 우리는 매개 변수 변수에 할당을했고 컴파일러는 상수 1에서 0으로 사용했던 메모리 위치의 데이터를 즐겁게 변경했습니다.
나중에 관련되지 않은 많은 함수가 리터럴 값 1과 비교하여 테스트가 실패한 코드를 갖게되었습니다. 나는 디버거에서 가장 오랫동안 그 코드를 쳐다 봤던 것을 기억한다. 나는 변수의 값을 출력 할 것이고, 1이 될 것이지만 'if (foo .EQ. 1)'테스트는 실패 할 것입니다. 디버거에게 1의 값이 무엇이라고 생각하는지 출력하도록 요청하는 데 오랜 시간이 걸렸습니다. 그런 다음 상수 1이 0이 될 때를 찾기 위해 코드를 추적하기 위해 많은 머리카락을 잡아 당겼습니다.
그다지 힘들지는 않지만 발견되었을 때 많이 웃었습니다.
온라인 상점을 위해 연중 무휴 주문 처리 시스템을 유지하고있을 때 고객이 주문이 "잘렸다"고 불평했습니다. 그는 그가 주문한 주문에는 실제로 N 개의 포지션이 포함되어 있지만 시스템은 아무런 경고없이 훨씬 적은 포지션을 받아 들였다고 주장했습니다.
시스템을 통해 주문 흐름을 추적 한 후 다음과 같은 사실이 밝혀졌습니다. 주문 항목을 데이터베이스에 저장하는 저장 프로 시저가 있습니다. 다음 (product-id, quantity, price)
과 같이 트리플 목록을 인코딩 한 문자열로 주문 항목 목록을 허용했습니다 .
"<12345, 3, 19.99> <56452, 1, 8.99> <26586, 2, 12.99>"
이제 저장 프로 시저의 작성자는 일반 구문 분석 및 루핑과 같은 것에 의존하기에는 너무 똑똑했습니다. 그래서 그는 "<"
으로 "insert into ... values ("
및 ">"
로 대체하여 문자열을 SQL 다중 삽입 문으로 직접 변환했습니다 ");"
. 결과 문자열을 varchar (8000) 변수에 저장하지 않았다면 모두 훌륭하고 멋졌습니다!
무슨 일이 있었는지 그의 "insert ...; insert ...;"
문자가 8000 번째 문자에서 잘 렸고 그 특정 순서에 insert
대해 잘린 SQL이 구문 적으로 올바른 상태를 유지 하도록 s 사이에 바로 발생할 수있을만큼 "운이 좋았습니다" .
나중에 나는 sp의 저자가 내 상사라는 것을 알았습니다.
나는 당신이 긴 보스 전에서 싸워서 승리 한 후에야 발생하는 콘솔 게임에 버그가있었습니다. 그리고 5 번에서 1 번 정도 밖에되지 않았습니다. 그것이 트리거되었을 때, 그것은 하드웨어를 100 % 쐐기로 고정시키고 외부 세계와 대화 할 수 없게됩니다. 조금도.
내가 만난 가장 수줍은 버그였습니다. 보스 전투를 수정, 자동화, 계측 또는 디버깅하면 버그가 숨겨 질 것입니다 (물론 버그가 숨겨 졌는지 확인하려면 10-20 번 실행해야합니다).
결국 2 ~ 3 일 동안 계속해서 코드를 읽음으로써 문제 (캐시 / DMA / 인터럽트 경쟁 문제)를 발견했습니다.
이것은 C ++와 디지털 시계가 꽤 깔끔하다고 생각했을 때 돌아 왔습니다.
어려운 메모리 누수를 해결할 수 있다는 평판을 얻었습니다. 다른 팀은 추적 할 수없는 누출이있었습니다. 그들은 나에게 조사를 요청했습니다.
이 경우 COM 개체였습니다. 시스템의 핵심에는 거의 똑같이 보이는 많은 꼬인 작은 COM 개체를 제공하는 구성 요소가있었습니다. 각각의 하나는 일에 대한 책임 각각의 다양한 클라이언트에게 물려 받았습니다 AddRef()
과 Release()
동일한 횟수.
누가 each를 호출 AddRef
했는지, 그리고 그들이 Release
d 를 가지고 있는지 자동으로 계산하는 방법은 없었습니다 .
디버거에서 며칠을 보냈고 작은 종이에 16 진수 주소를 적었습니다. 내 사무실은 그들로 덮여있었습니다. 마침내 나는 범인을 찾았습니다. 저에게 도움을 요청한 팀은 매우 감사했습니다.
다음날 GC 언어로 전환했습니다. *
(* 사실은 아니지만 이야기의 좋은 결말이 될 것입니다.)
Sun Microsystems의 Bryan Cantrill은 dtrace라는 도구를 사용하여 추적 한 버그에 대해 훌륭한 Google Tech Talk를 제공했습니다.
The Tech Talk 는 재미 있고 괴상하고 유익하며 매우 인상적입니다 ( 약 78 분 길이 ).
버그가 무엇인지에 대해서는 여기서 스포일러를주지 않을 것이지만 그는 53:00 경에 범인을 밝히기 시작합니다.
최근에 거래 응용 프로그램에 추가 한 몇 가지 새로운 기능을 테스트하는 동안 특정 유형의 거래 결과를 표시하는 코드가 제대로 작동하지 않는 것을 발견했습니다. 소스 제어 시스템을 살펴본 결과이 버그가 최소 1 년 동안 존재했음을 알 수 있었으며 거래자가 발견 한 적이 없다는 사실에 놀랐습니다.
잠시 수수께끼를 풀고 동료와 확인한 후 버그를 수정하고 새로운 기능을 테스트했습니다. 약 3 분 후 전화가 울 렸습니다. 라인의 다른 쪽 끝에는 자신의 거래 중 하나가 올바르게 표시되지 않는다고 불평하는 분노한 거래자가있었습니다.
추가 조사를 통해 거래자가 3 분 전에 코드에서 발견 한 것과 똑같은 버그가 발생했음을 깨달았습니다. 이 버그는 개발자가 와서 발견하여 실제 공격을받을 수 있기를 기다리면서 1 년 동안 존재했습니다.
이것은 Schroedinbug 로 알려진 버그 유형의 좋은 예입니다 . 우리 대부분은이 독특한 존재에 대해 들어 봤지만 실제로 야생에서 만나면 기분이 으스스합니다.
떠오르는 가장 어려운 두 가지 버그는 모두 동일한 유형의 소프트웨어에 있었으며 하나는 웹 기반 버전에 있었고 하나는 Windows 버전에있었습니다.
이 제품은 평면도 뷰어 / 편집기입니다. 웹 기반 버전에는 데이터를 SVG로로드하는 플래시 프런트 엔드가 있습니다. 자, 이것은 잘 작동했고 가끔 브라우저가 멈출 것입니다. 몇 개의 도면에서만, 그리고 약간의 도면 위로 마우스를 움직일 때만 가능합니다. 1.5MB의 SVG 데이터를 포함하는 단일 드로잉 레이어로 문제를 좁혔습니다. 데이터의 하위 섹션, 하위 섹션 만 취하면 중단이 발생하지 않았습니다. 결국 문제는 아마도 파일에 버그를 일으킨 여러 섹션이 있다는 것입니다. 물론 레이어의 섹션을 무작위로 삭제하고 버그를 테스트 한 후 문제가되는 드로잉 문 조합을 발견했습니다. SVG 생성기에서 해결 방법을 작성했으며 액션 스크립트 줄을 변경하지 않고 버그가 수정되었습니다.
Delphi로 작성된 Windows 쪽의 동일한 제품에서 비슷한 문제가 발생했습니다. 여기서 제품은 autocad DXF 파일을 가져 와서 내부 도면 형식으로 가져와 사용자 정의 도면 엔진에서 렌더링합니다. 이 가져 오기 루틴은 특히 효율적이지는 않지만 (많은 하위 문자열 복사를 사용함) 작업을 완료합니다. 이 경우에만 그렇지 않았습니다. 5MB 파일은 일반적으로 20 초 내에 가져 오지만 한 파일에서는 메모리 사용량이 기가 바이트 이상으로 팽창했기 때문에 20 분이 걸렸습니다. 처음에는 일반적인 메모리 누수처럼 보였지만 메모리 누수 도구는이를 깨끗하게보고했고 수동 코드 검사도 아무것도 나타나지 않았습니다. 문제는 Delphi 5의 메모리 할당 자의 버그로 밝혀졌습니다. 이 특정 파일이 정식으로 다시 생성하는 일부 조건에서는 심각한 메모리 조각화가 발생하기 쉽습니다. 시스템은 계속해서 큰 문자열을 할당하려고 시도하고 가장 많이 할당 된 메모리 블록 위를 제외하고는 문자열을 넣을 곳을 찾지 못합니다. 새로운 메모리 할당 라이브러리를 통합하여 가져 오기 코드를 변경하지 않고 버그를 수정했습니다.
되돌아 보면, 가장 어려운 버그는 문제가 발생한 버그와 시스템의 다른 부분을 변경하는 것과 관련된 수정 사항 인 것 같습니다.
클라이언트의 애완용 토끼 토끼가 이더넷 케이블을 갉아 먹었을 때. 예. 별로 였어.
장치 디버거에서 매우 나쁜 플랫폼에 버그가 있습니다. 코드에 printf 를 추가 하면 장치 에서 충돌이 발생 합니다. 그런 다음 printf의 위치가 아닌 다른 지점에서 충돌합니다. printf를 이동하면 충돌이 이동하거나 사라집니다 . 실제로 간단한 명령문을 재정렬하여 해당 코드를 변경하면 변경 한 코드와 관련이없는 곳에서 충돌이 발생합니다.
우리 플랫폼의 재배치 자에 버그 가있는 것으로 밝혀졌습니다 . 재배치자는 ZI 섹션을 0으로 초기화하는 것이 아니라 재배치 테이블을 사용하여 값을 초기화했습니다. 따라서 재배치 테이블이 바이너리에서 변경 될 때마다 버그가 이동합니다. 따라서 단순히 printf를 추가하면 버그에 대한 재배치 테이블이 변경됩니다.
이것은 내가 컴퓨터 상점에서 일할 때 나에게 일어났습니다.
한 고객이 어느 날 상점에 와서 그의 새 컴퓨터가 저녁과 밤에는 잘 작동했지만 정오 나 늦은 아침에는 전혀 작동하지 않는다고 말했습니다. 문제는 그 때 마우스 포인터가 움직이지 않는다는 것입니다.
우리가 가장 먼저 한 일은 그의 마우스를 새 것으로 바꾸는 것이었지만 문제는 해결되지 않았습니다. 물론 두 마우스 모두 결함없이 매장에서 작동했습니다.
몇 번의 시도 끝에 특정 브랜드와 마우스 모델에 문제가 있음을 발견했습니다. 고객 워크 스테이션은 매우 큰 창에 가까웠고 정오에는 마우스가 직사광선을 받고있었습니다. 플라스틱은 너무 얇아서 그 상황에서 반투명 해졌고 햇빛이 작업을위한 광 기계 바퀴를 막았습니다.
우리 팀은 CGI 기반의 다중 스레드 C ++ 웹 앱을 상속 받았습니다. 주요 플랫폼은 Windows였습니다. 멀리 떨어진 보조 플랫폼은 Posix 스레드가있는 Solaris였습니다. 솔라리스의 안정성은 어떤 이유로 재앙이었습니다. 우리는 영업 직원이 Windows 버전을 성공적으로 푸시하는 동안 1 년 넘게 문제를 살펴본 다양한 사람들이있었습니다.
증상은 한심한 안정성이었습니다. 거의 운율이나 이유없이 광범위한 시스템 충돌이 발생했습니다. 이 앱은 Corba와 자체 개발 프로토콜을 모두 사용했습니다. 한 개발자는 필사적 인 조치로 전체 Corba 하위 시스템을 제거했습니다. 운이 없습니다.
마지막으로 한 선임 개발자가 아이디어에 대해 큰 소리로 궁금해했습니다. 우리는 그것을 조사하고 결국 문제를 발견했습니다. 솔라리스에는 실행 파일의 스택 크기를 조정하기위한 컴파일 타임 (또는 런타임?) 매개 변수가있었습니다. 잘못 설정되었습니다. 너무 작습니다. 따라서 앱은 스택이 부족하고 전체 적 청어 인 스택 트레이스를 인쇄했습니다.
진정한 악몽이었습니다.
교훈:
- 브레인 스토밍, 브레인 스토밍, 브레인 스토밍
- 다른 방치 된 플랫폼에서 무언가가 엉망이되는 것은 아마도 환경 플랫폼의 속성 일 것입니다.
- Beware of problems that are transferred from developers who leave the team. If possible, contact the previous people on personal basis to garner info and background. Beg, plead, make a deal. The loss of experience must be minimized at all costs.
Adam Liss's message above talking about the project we both worked on, reminded me of a fun bug I had to deal with. Actually, it wasn't a bug, but we'll get to that in a minute.
Executive summary of the app in case you haven't seen Adam message yet: sales-force automation software...on laptops...end of the day they dialed up ...to synchronize with the Mother database.
One user complained that every time he tried to dial in, the application would crash. The customer support folks went through all their usually over-the-phone diagnostic tricks, and they found nothing. So, they had to relent to the ultimate: have the user FedEx the laptop to our offices. (This was a very big deal, as each laptop's local database was customized to the user, so a new laptop had to be prepared, shipped to the user for him to use while we worked on his original, then we had to swap back and have him finally sync the data on first original laptop).
So, when the laptop arrived, it was given to me to figure out the problem. Now, syncing involved hooking up the phone line to the internal modem, going to the "Communication" page of our app, and selecting a phone number from a Drop-down list (with last number used pre-selected). The numbers in the DDL were part of the customization, and were basically, the number of the office, the number of the office prefixed with "+1", the number of the office prefixed with "9,,," in case they were calling from an hotel etc.
So, I click the "COMM" icon, and pressed return. It dialed in, it connected to a modem -- and then immediately crashed. I tired a couple more times. 100% repeatability.
So, a hooked a data scope between the laptop & the phone line, and looked at the data going across the line. It looked rather odd... The oddest part was that I could read it!
The user had apparently wanted to use his laptop to dial into a local BBS system, and so, change the configuration of the app to use the BBS's phone number instead of the company's. Our app was expecting our proprietary binary protocol -- not long streams of ASCII text. Buffers overflowed -- KaBoom!
The fact that a problem dialing in started immediately after he changed the phone number, might give the average user a clue that it was the cause of the problem, but this guy never mentioned it.
I fixed the phone number, and sent it back to the support team, with a note electing the guy the "Bonehead user of the week". (*)
(*) OkOkOk... There's probably a very good chance what actually happened in that the guy's kid, seeing his father dial in every night, figured that's how you dial into BBS's also, and changed the phone number sometime when he was home alone with the laptop. When it crashed, he didn't want to admit he touched the laptop, let alone broke it; so he just put it away, and didn't tell anyone.
It was during my diploma thesis. I was writing a program to simulate the effect of high intensity laser on a helium atom using FORTRAN.
One test run worked like this:
- Calculate the intial quantum state using program 1, about 2 hours.
- run the main simulation on the data from the first step, for the most simple cases about 20 to 50 hours.
- then analyse the output with a third program in order to get meaningful values like energy, tork, momentum
These should be constant in total, but they weren't. They did all kinds of weird things.
After debugging for two weeks I went berserk on the logging and logged every variable in every step of the simulation including the constants.
That way I found out that I wrote over an end of an array, which changed a constant!
A friend said he once changed the literal 2 with such a mistake.
A deadlock in my first multi-threaded program!
It was very tough to find it because it happened in a thread pool. Occasionally a thread in the pool would deadlock but the others would still work. Since the size of the pool was much greater than needed it took a week or two to notice the first symptom: application completely hung.
I have spent hours to days debugging a number of things that ended up being fixable with literally just a couple characters.
Some various examples:
ffmpeg has this nasty habit of producing a warning about "brainfart cropping" (referring to a case where in-stream cropping values are >= 16) when the crop values in the stream were actually perfectly valid. I fixed it by adding three characters: "h->".
x264 had a bug where in extremely rare cases (one in a million frames) with certain options it would produce a random block of completely the wrong color. I fixed the bug by adding the letter "O" in two places in the code. Turned out I had mispelled the name of a #define in an earlier commit.
My first "real" job was for a company that wrote client-server sales-force automation software. Our customers ran the client app on their (15-pound) laptops, and at the end of the day they dialed up to our unix servers to synchronize with the Mother database. After a series of complaints, we found that an astronomical number of calls were dropping at the very beginning, during authentication.
After weeks of debugging, we discovered that the authentication always failed if the incoming call was answered by a getty process on the server whose Process ID contained an even number followed immediately by a 9. Turns out the authentication was a homebrew scheme that depended on an 8-character string representation of the PID; a bug caused an offending PID to crash the getty, which respawned with a new PID. The second or third call usually found an acceptable PID, and automatic redial made it unnecessary for the customers to intervene, so it wasn't considered a significant problem until the phone bills arrived at the end of the month.
The "fix" (ahem) was to convert the PID to a string representing its value in octal rather than decimal, making it impossible to contain a 9 and unnecessary to address the underlying problem.
Basically, anything involving threads.
I held a position at a company once in which I had the dubious distinction of being one of the only people comfortable enough with threading to debug nasty issues. The horror. You should have to get some kind of certification before you're allowed to write threaded code.
I heard about a classic bug back in high school; a terminal that you could only log into if you sat in the chair in front of it. (It would reject your password if you were standing.)
It reproduced pretty reliably for most people; you could sit in the chair, log in, log out... but if you stand up, you're denied, every time.
Eventually it turned out some jerk had swapped a couple of adjacent keys on the keyboard, E/R and C/V IIRC, and when you sat down, you touch-typed and got in, but when you stood, you had to hunt 'n peck, so you looked at the incorrent labels and failed.
While I don't recall a specific instance, the toughest category are those bugs which only manifest after the system has been running for hours or days, and when it goes down, leaves little or no trace of what caused the crash. What makes them particularly bad is that no matter how well you think you've reasoned out the cause, and applied the appropriate fix to remedy it, you'll have to wait for another few hours or days to get any confidence at all that you've really nailed it.
Our network interface, a DMA-capable ATM card, would very occasionally deliver corrupted data in received packets. The AAL5 CRC had checked out as correct when the packet came in off the wire, yet the data DMAd to memory would be incorrect. The TCP checksum would generally catch it, but back in the heady days of ATM people were enthused about running native applications directly on AAL5, dispensing with TCP/IP altogether. We eventually noticed that the corruption only occurred on some models of the vendor's workstation (who shall remain nameless), not others.
By calculating the CRC in the driver software we were able to detect the corrupted packets, at the cost of a huge performance hit. While trying to debug we noticed that if we just stored the packet for a while and went back to look at it later, the data corruption would magically heal itself. The packet contents would be fine, and if the driver calculated the CRC a second time it would check out ok.
We'd found a bug in the data cache of a shipping CPU. The cache in this processor was not coherent with DMA, requiring the software to explicitly flush it at the proper times. The bug was that sometimes the cache didn't actually flush its contents when told to do so.
참고URL : https://stackoverflow.com/questions/169713/whats-the-toughest-bug-you-ever-found-and-fixed
'developer tip' 카테고리의 다른 글
bool을 int로 사용하는 것이 Pythonic입니까? (0) | 2020.11.22 |
---|---|
UIImage 둥근 모서리 (0) | 2020.11.22 |
C ++ 애드온의 'MediaStream'개체에서 오디오 데이터를 읽는 방법 (0) | 2020.11.22 |
XCode 스토리 보드 병합 (0) | 2020.11.22 |
Safari 7 응용 프로그램 캐시가 작동하지 않습니다. (0) | 2020.11.22 |