schrödinbug 란 무엇입니까?


52

위키 페이지 는 다음을 알려줍니다.

schrödinbug는 누군가가 소스 코드를 읽거나 비정상적인 방식으로 프로그램을 사용한 후에 만 ​​처음부터 작동해서는 안된다는 것을 알리는 버그입니다.이 시점에서 프로그램은 수정 될 때까지 모든 사람의 작업이 즉시 중지됩니다. Jargon File은 다음과 같이 덧붙입니다. "하지만 ... 불가능한 것 같지만 일부 프로그램은 잠재적 인 schrödinbugs를 몇 년 동안 가지고있었습니다."

대화 내용은 매우 모호합니다 ..

누군가 슈 뢰딘 버그가 어떻게 가상인지 (가상적 / 실제 상황과 같은) 예를 제공 할 수 있습니까?


15
따옴표는 농담으로 알려집니다.

11
Shrodinger의 고양이에 대해 알고 있다면 shrodinbug를 더 잘 이해한다고 생각합니다. en.wikipedia.org/wiki/Shrodingers_cat
Eimantas

1
@Eimantas 나는 지금 실제로 더 혼란스러워하지만 그것은 흥미로운 기사이다 :)

답변:


82

내 경험에 따르면 패턴은 다음과 같습니다.

  • 종종 몇 년 동안 시스템 작동
  • 오류가보고되었습니다
  • 개발자는 오류를 조사하고 완전히 결함이있는 것처럼 보이는 코드를 찾아서 "작동하지 않았을 것"이라고 선언합니다.
  • 버그가 수정되고 작동하지 않았지만 수년 동안 할 수 없었던 코드의 전설이 커짐

여기서 논리적으로합시다. 결코 작동 할 수 없었던 코드 ... 결코 작동하지 않았을 수 있습니다 . 그것이 효과 있었다면 진술은 거짓입니다.

따라서 설명 된 것과 같은 버그 (결함이있는 코드가 작동을 멈추는 것을 관찰하는 것)는 특허 적으로 의미가 없다고 말할 것입니다 .

실제로 일어난 일은 다음 두 가지 중 하나입니다.

1) 개발자는 코드를 완전히 이해하지 못했습니다 . 이 경우 코드는 일반적으로 엉망이며 코드의 어딘가에 외부 조건에 대한 중요하지만 분명하지 않은 감도가 있습니다 (일부 기능은 작지만 중요한 방식으로 작동하는 특정 OS 버전 또는 구성). 이 외부 조건은 변경되어 (예 : 서버 업그레이드 또는 관련이없는 것으로 변경됨) 코드가 손상됩니다.

그런 다음 개발자는 코드를 살펴보고 기록 컨텍스트를 이해하지 못하거나 가능한 모든 종속성 및 시나리오를 추적 할 시간이 없어서 코드가 작동하지 않고 다시 작성할 수 없다고 선언했습니다.

이 상황에서 여기서 이해해야 할 것은 "한 번도 할 수 없었습니다"라는 생각이 틀렸다는 것입니다.

그것은 다시 쓰는 것이 나쁜 일이라고 말하는 것은 아닙니다. 종종 잘못된 일이 무엇인지 정확히 아는 것이 좋지만 시간이 많이 걸리고 코드 섹션을 다시 쓰는 것이 더 빠르며 문제가 해결되었는지 확인할 수 있습니다.

2) 실제로는 작동하지 않았으며 아무도 눈치 채지 못했습니다 . 이것은 특히 대규모 시스템에서 놀랍게도 일반적입니다. 이 경우 새로운 누군가가 이전에 아무도하지 않은 방식으로 일을 시작하거나 비즈니스 프로세스가 변경되어 이전에 사소한 우연한 사례를 주요 프로세스로 가져 왔으며 실제로는 전혀 작동하지 않았거나 전혀 작동하지 않은 일부 시간)을 찾아보고합니다.

개발자는이를보고 "작동 한 적이 없었습니다"라고 선언하지만 사용자는 "넌센스, 우리는 몇 년 동안 사용해 왔습니다"라고 말하지만 옳은 것이지만 관련이없는 것으로 간주합니다 (일반적으로 개발자들은 "아, 네, 우리가해야합니까 가고있는 시점에서 정확한 상태를 발견 한 것을 지금 전에하지 않았다"변경되었습니다).

여기서 개발자는 옳습니다. 결코 작동하지 않았으며 작동하지 않았습니다.

그러나 어느 경우 든 두 가지 중 하나가 사실입니다.

  • "그것은 결코 일할 수 없었습니다"라는 주장은 사실이며 결코 효과가 없었습니다.
  • 그것은 효과가 있었고 "그것은 결코 일할 수 없었습니다"라는 진술은 거짓이며 코드와 그 의존성에 대한 이해가 부족합니다 (보통 합리적인)

1
저에게 너무나 자주
일어납니다

2
위대한 이러한 상황 리얼리즘에 대한 통찰력
StuperUser

1
보통 "WTF"순간의 결과라고 생각합니다. 나는 한 번했다. 필자가 작성한 일부 코드를 다시 읽고 최근에 발견 된 버그로 인해 전체 앱이 고장 났음을 깨달았습니다. 실제로, 추가 검사 후, 내가 쓴 다른 구성 요소는 너무 좋았으므로 실수를 보완했습니다.
Thaddee Tyl

1
@Thaddee-전에는 보았지만 코드 모듈에서 서로를 취소하여 실제로 작동하는 두 가지 버그를 보았습니다. 둘 중 하나를보고 고장이 나지만 함께 잘 지 냈습니다.
존 홉킨스

7
@ Jon Hopkins : 또한 2 개의 버그가 서로 상쇄되는 경우가 있는데 정말 놀랍습니다. 나는 버그를 발견하고, "그것은 결코 일할 수 없었습니다"라는 악명 높은 진술을 입었고, 어쨌든 그것이 효과가 있었던 이유를 찾기 위해 더 깊이 들여다 보았고, 적어도 대부분의 경우 첫 번째 버그를 수정 한 다른 버그를 발견했습니다. 나는 그 발견에 정말 놀랐고, 버그 중 하나만 있으면 결과는 치명적이었을 것입니다!
Alexis Dufrenoy

54

모두가 작동해서는 안되는 코드에 대해 언급했기 때문에 약 8 년 전에 죽어가는 VB3 프로젝트에서 .net으로 변환 된 사례에 대해 설명하겠습니다. 불행히도 .net 버전이 완성 될 때까지 프로젝트를 최신 상태로 유지해야했습니다. VB3을 원격으로 이해 한 사람은 저뿐이었습니다.

매 계산마다 수백 번 불리는 매우 중요한 기능이있었습니다. 장기 연금 계획에 대한 월별이자를 계산했습니다. 흥미로운 부분을 재현하겠습니다.

Function CalculateMonthlyInterest([...], IsYearlyInterestMode As Boolean, [...]) As Double
    [about 30 lines of code]
    If IsYearlyInterestMode Then
        [about 30 lines of code]
        If Not IsYearlyInterestMode Then
            [about 30 lines of code (*)]
        End If
    End If
End Function

별표가 표시된 부분에는 가장 중요한 코드가 있습니다. 실제 계산을 수행 한 유일한 부분이었습니다. 분명히 이것은 결코 효과가 없었을 것입니다.

그것은 디버깅을 많이했다,하지만 난 결국 원인을 발견 IsYearlyInterestMode했다 True, 그리고 Not IsYearlyInterestMode또한 사실이었다. 그것은 선을 따라 어딘가에 정수로 캐스트 한 다음 true로 설정하는 함수에서 그것을 증가 시켰기 때문입니다 (0 False이면 VB True인 1로 설정 되므로 논리를 볼 수 있습니다) 그런 다음 부울로 다시 캐스팅하십시오. 그리고 나는 결코 일어날 수없는 조건으로 항상 남아있었습니다.


7
에필로그 : 나는 그 기능을 결코 고치지 않았다. 방금 실패한 통화 사이트를 패치하여 다른 모든 사이트와 마찬가지로 2를 보냅니다.
구성자

사람들이 코드를 잘못 해석 할 때 사용된다는 것을 의미합니까?
Pacerier

1
@Pacerier : 더 자주 코드가 엉망인 경우 우연히 올바르게 작동합니다. 필자의 예제에서는 개발자가 IsYearlyInterestMode사실이 아닌 것으로 평가 하려고 하지 않았습니다. 몇 줄을 추가 한 최초 개발자 (S를 포함하여 if실제로는 어떻게 작동하는지 이해하지 못함) 그냥 작동했기 때문에 충분했습니다.
구성자

16

실제 예제를 모르지만 예제 상황으로 단순화하려면 다음을 수행하십시오.

  • 응용 프로그램은 코드를 실행할 수없는 조건에서 코드를 실행하지 않기 때문에 버그가 한동안 나타나지 않습니다.
  • 누군가 정상적인 사용 이외의 작업을 수행하거나 소스를 검사하여이를 알 수 있습니다.
  • 버그가 발견되었으므로 버그가 수정 될 때까지 정상적인 조건까지 응용 프로그램이 실패합니다.

버그는 이전의 정상적인 조건에서 오류를 발생시키는 응용 프로그램의 일부 상태를 손상시키기 때문에 발생할 수 있습니다.


4
한 가지 설명은 소프트웨어에 임의의 오류가 발생하여 아무도 정신적으로 연결할 수 없다는 것입니다. 따라서 이러한 오류는 임의의 하드웨어 오류와 같은 자연스러운 원인으로 생각되었습니다. 소스 코드가 읽 히면 사람들은 이제 모든 사전 임의 오류를이 원인과 연관시킬 수 있으며, 처음부터 작동하지 않았 음을 알 수 있습니다.
rwong

4
두 번째 설명은 소프트웨어에 책임 체인 패턴으로 구현 된 부분이 있다는 것입니다. 하나의 핸들러에 중대한 버그가 있음에도 불구하고 각 핸들러는 강력한 방식으로 작성됩니다. 이제 첫 번째 핸들러는 항상 실패하지만 두 번째 핸들러 (책임이 중복 됨)로 인해 동일한 작업을 수행하려고 시도하면 전체 작업이 성공한 것으로 보입니다. 책임 영역 변경과 같이 두 번째 모듈에 변경이있는 경우 실제 버그가 다른 위치에 있더라도 전체적인 실패가 발생할 수 있습니다.
rwong

13

실제 사례. 코드를 보여줄 수는 없지만 대부분의 사람들은 이에 관련됩니다.

우리는 내가 일하는 큰 유틸리티 함수 라이브러리를 가지고 있습니다. 어느 날 나는 특정 일을하는 기능을 찾고 있는데 Frobnicate()그것을 사용하려고합니다. 어 : Frobnicate()항상 오류 코드를 반환합니다.

구현을 살펴보면 Frobnicate()항상 실패하게 만드는 몇 가지 기본 논리 오류가 있습니다. 소스 컨트롤에서 함수가 작성된 이후 수정되지 않았 음을 알 수 있습니다. 이는 함수가 의도 한대로 작동 하지 않았 음을 의미합니다 . 아무도 이것을 눈치 채지 못한 이유는 무엇입니까? 나머지 소스 참여를 검색하여 기존의 모든 발신자가 Frobnicate()반환 값을 무시하고 있다는 것을 알았습니다 (따라서 자체 미묘한 버그가 포함되어 있음). 반환 값을 확인하기 위해 해당 함수를 변경하면 실패하기 시작합니다.

Jon Hopkins가 그의 답변에서 언급 한 일반적인 조건 # 2의 사례이며 대형 내부 라이브러리에서 우울하게 흔합니다.


... 이것은 외부 라이브러리를 사용할 수있는 곳이라면 내부 라이브러리를 작성하지 않는 것이 좋습니다. 더 많은 테스트를 거쳤으므로 그러한 놀라운 놀라움은 훨씬 적습니다 (어쨌든 수정하면 오픈 소스 라이브러리가 바람직합니다).
Jan Hudec

예, 그러나 프로그래머가 라이브러리 오류가 아닌 리턴 코드를 무시하면 (그런데, 마지막으로 printf()? 의 retcode를 확인한 시간은 언제입니까?)
JensG

이것이 바로 확인 된 예외가 발명 된 이유입니다.
Kevin Krumwiede

10

다음은 일부 시스템 코드에서 본 Schrödinbug입니다. 루트 데몬은 커널 모듈과 통신해야합니다. 커널 코드는 파일 디스크립터를 생성합니다 :

int pipeFDs[1];

그런 다음 명명 된 파이프에 연결될 파이프를 통해 통신을 설정합니다.

int pipeResult = pipe(pipeFDs);

작동합니다. pipe()두 개의 파일 디스크립터를 배열에 씁니다. 그러나 공간은 하나뿐입니다. 그러나 약 7 년 동안이 했던 일을; 파일 디스크립터가 된 메모리에서 사용되지 않은 공간보다 먼저 배열이 발생했습니다.

그런 다음 어느 날 코드를 새로운 아키텍처로 이식해야했습니다. 그것은 작동을 멈추었고 결코 작동해서는 안되는 버그가 발견되었습니다.


5

Schrödinbug의 기록은 Heisenbug입니다. 이 버그는 조사 및 / 또는 수정을 시도 할 때 사라지는 버그를 나타냅니다.

Heisenbugs는 디버거가로드 될 때 실행하고 숨기는 신화적인 영리한 작은 blighters이지만 시청을 멈 추면 목공에서 나옵니다.

실제로 이러한 문제는 대개 다음 중 하나 이상으로 인해 발생합니다.

  • 컴파일 된 코드 -DDEBUG가 릴리스 빌드와 다른 수준으로 최적화 되는 최적화의 영향
  • 실제 통신 버스 또는 인터럽트로 인해 미묘한 타이밍 차이가 시뮬레이션 된 "완벽한"더미로드와 미묘하게 다릅니다

둘 다 릴리스 장비에서 릴리스 코드 테스트의 중요성과 에뮬레이터를 사용한 단위 / 모듈 / 시스템 테스트의 중요성을 강조합니다.


S.Lote의 답변과 delnan의 의견을 게시하기 전에 왜 알지 못했습니까?
Andrew

나는 약간의 경험이 있지만 몇 가지를 발견했습니다. Android NDK 환경에서 일하고있었습니다. 디버거가 중단 점을 찾으면 C ++ 스레드가 아닌 Java 스레드 만 중지하고 C ++에서 요소가 초기화 되었기 때문에 일부 호출이 가능해졌습니다. 디버거없이 남겨두면 Java 코드가 C ++보다 빠르며 아직 초기화되지 않은 값을 사용하려고합니다.
MLProgrammer-CiM

몇 달 전에 Django 데이터베이스 API 사용법에서 Heisenbug를 발견했습니다 . When DEBUG = True, 원시 SQL 쿼리에 대한 "매개 변수"인수의 이름이 변경됩니다. 우리는 그것 때문에 베타 사이트로 밀어 시간이었다 때 완전히 파산 쿼리의 길이, 선명도라는 키워드를 인수로 사용하고있었습니다DEBUG = False
Izkata

2

나는 몇 가지 Schödinbugs를 보았고 항상 같은 이유로 :

회사 정책에 따라 모든 사람이 프로그램을 사용해야했습니다.
아무도 실제로 그것을 사용하지 않았습니다 (주로 훈련이 없었기 때문에).
그러나 그들은 경영진에게 이것을 말할 수 없었습니다. "모두이 프로그램을 2 년 동안 사용해 왔으며 오늘까지는이 버그가 발생하지 않았습니다."
이 프로그램은 소수의 사용자 (프로그램을 작성한 개발자 포함)를 제외하고는 실제로 작동하지 않았습니다.

어떤 경우에는 프로그램이 많은 테스트를 받았지만 실제 데이터베이스는 아닙니다 (너무 민감한 것으로 간주되어 가짜 버전이 사용되었습니다).


1

나는 내 역사에서 예를 들었습니다. 이것은 약 25 년 전입니다. 저는 Turbo Pascal에서 기초 그래픽 프로그래밍을하는 어린이였습니다. TP에는 화면의 영역을 포인터 기반 메모리 블록에 복사 한 다음 다른 곳에서 블리트 할 수있는 일부 기능이 포함 된 BGI라는 라이브러리가 있습니다. 흑백 화면에서 xor-blitting과 결합하여 간단한 애니메이션을 수행하는 데 사용될 수 있습니다.

한 걸음 더 나아가 스프라이트를 만들고 싶었습니다. 큰 블록과 컨트롤을 그려서 색을 칠하는 프로그램을 작성했습니다.이를 픽셀로 재현하여 스프라이트를 만드는 간단한 드로잉 프로그램을 생성하여 메모리에 복사 할 수 있습니다. 이 번쩍이는 스프라이트를 사용하는 데에는 한 가지 문제가 있었는데, 다른 프로그램이 읽을 수 있도록 파일에 저장해야했습니다. 그러나 TP는 포인터 기반 메모리 할당을 직렬화하는 방법이 없었습니다. 평평한 매뉴얼은 파일에 쓸 수 없다고 명시했습니다.

성공적으로 파일에 쓰는 코드 조각을 생각해 냈습니다. 그리고 게임을 만드는 도중에 드로잉 프로그램에서 스프라이트를 배경으로 테스트하는 테스트 프로그램을 작성하기 시작했습니다. 그리고 그것은 아름답게 작동했습니다. 그러나 다음날은 작동을 멈췄습니다. 그것은 깨지기 쉬운 혼란을 보여주었습니다. 다시는 효과가 없었습니다. 나는 새로운 스프라이트를 만들었고 완벽하게 작동했습니다.

오랜 시간이 걸렸지 만 결국 무슨 일이 있었는지 알았습니다. 생각대로, 드로잉 프로그램은 복사 된 픽셀 데이터를 파일로 저장하지 않았습니다. 포인터 자체를 저장하는 것이 었습니다. 다음 프로그램이 파일을 읽을 때 동일한 메모리 블록에 대한 포인터로 끝났습니다. 여기에는 마지막 프로그램이 작성한 내용이 포함되어 있습니다 (MS-DOS에 있었고 메모리 관리는 없었습니다). 그러나 그것은 재부팅 할 때까지 또는 동일한 메모리 영역을 재사용 한 것을 실행할 때까지 효과가 있었으며 비디오 메모리 블록에 전혀 관련이없는 데이터를 뭉개 서 혼란에 빠졌습니다.

그것은 결코 작동해서는 안되며 결코 작동하지 않는 것처럼 보였을 것입니다 (그리고 실제 OS에서는 그렇지 않았을 것입니다). 그러나 여전히 작동했으며, 일단 고장 나면 고장났습니다.


0

사람들이 디버거를 사용할 때 항상 발생합니다.

디버깅 환경은 실제 (디버거 없음) 프로덕션 환경과 다릅니다.

디버거로 실행하면 디버거의 스택 프레임이 버그를 가리기 때문에 스택 오버플로와 같은 것을 숨길 수 있습니다.


디버거에서 실행되는 코드와 컴파일 할 때의 코드 차이를 언급한다고 생각하지 않습니다.
Jon Hopkins

26
그것은 schrödinbug가 아니며, heisenbug 입니다.

@delnan : IMO가 가장 중요합니다. 나는 알 수없는 자유도가 있기 때문에 그것이 불확실한 것으로 판명되었습니다. 나는 한 가지를 측정하는 것이 실제로 다른 것을 방해하는 것들 (예 : 경쟁 조건, 최적화 설정, 네트워크 대역폭 제한 등)에 대해 heisenbug를 예약하고 싶습니다
S.Lott

@ S.Lott : 설명하는 상황에는 스택 프레임 등을 엉망으로 바꾸어 관찰하는 것이 포함됩니다. (내가 본 최악의 예는 디버거가 단일 단계 모드에서 유효하지 않은 세그먼트 레지스터 값의로드를 평화롭게 "정확하게"실행하는 것이었다. 결과는 RTL의 일부 루틴이 보호 모드에있는 동안 실제 모드 포인터를로드하더라도 선적되었다 복사되고 역 참조되지 않았기 때문에 완벽하게 작동했습니다.)
Loren Pechtel

0

나는 진정한 schrodinbug를 본 적이 없으며 그것들이 존재할 수 있다고 생각하지 않습니다.

오히려, 몇 년 동안 숨어 있던 버그를 드러내는 무언가가 바뀌 었습니다. 변경된 내용은 계속 변경되므로 누군가 버그를 발견하는 동시에 버그가 계속 나타납니다.

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