반응형

제목: 하이젠버그 디버깅하기(Debugging Heisenbugs)

저자: Mick West

문서유형: 웹사이트 기사, 2008 3 23

소스: http://cowboyprogramming.com/2008/03/23/debugging-heisenbugs/

 

 

게임 개발 분야에서 하이젠버그의 원인을 조명해보고 이를 피하거나 추적하기 위한 가이드라인을 제시한 자료


 

하이젠버그(Heisenbug)

  • 디버그를 시도하면 사라지거나 동작을 바꾸는 버그 타입
  • 하이젠버그는 게임 개발에서도 흔하게 나타남(하위 레벨 코드에서 주로 발생)

 

하이젠버그의 원인

  • 일반 버그와 마찬가지로 하이젠버그의 원인도 다양하지만 특히 프로그래머의 통제 밖에 있는 무작위 인자(random factors)에 크게 의존함
  • 대표적인 예가 난수(random numbers) 생성에 의해 야기되는 버그(, 테이블 오버플로우 버그가 두 개의 특정 난수가 연달아 생성될 때만 발생함). , 생성되는 수가 게임 상태에 의해 영향을 받고 이 게임 상태는 사용자 입력에 의해 영향을 받으므로 어떤 임의의 수가 생성될지 예측할 수 없음. 이런 가능성을 없애기 위해서 난수생성기(random number generator)가 동일 숫자를 리턴하도록 만들고 버그가 여전히 발생하는지를 확인해 볼 수 있음
  • 하이젠버그를 야기하는 다른 무작위 인자로 아래를 들 수 있다
    -
    댕글링 포인터의 주소(the addresses of dangling pointers)
    -
    멀티쓰레드 알고리즘에서 데이터 프로세싱의 순서
    - DMA
    에 의해 underwritten unflushed 캐시의 콘텐츠
    -
    초기화가 안된(uninitialized) 메모리의 콘텐츠
    - GPU
    레지스터의 가정 상태(assumed state)
    -
    사용자 입력(특히 아날로그)
    -
    영구 스토리지(persistent storage)를 위한 readwrite 횟수
    -
    부적절하게 동기화된 메모리(휘발성 변수)에서 값의 지속성(persistence)
  • 하이젠버그의 주요 진단 기법은 임의성(randomness)/불확정성(indeterminism)의 모든 소스를 제거하려는 노력으로 볼 수 있음

 

초기화되지 않은 메모리(Uninitialized Memory)

  • 메모리가 할당될 때 또는 변수가 초기화될 때 어떤 값으로도 설정되지 않는 경우가 종종 있음. 해당 메모리를 사용하는 코드가 나름의 의미 있는 값으로 초기화를 하므로 대개는 이것이 문제가 되지 않음
  • 하지만 잘못 설계된 코드(또는 코드 확장이 미칠 영향에 대한 충분한 이해 없이 확장된 코드)로 인해 메모리가 미처 초기화되기 전에 사용되는 일이 생길 수 있음. 초기화되지 않은 값이 일반적으로 동일한 값이지만 특정 상황하에서 관계 없는 로직 흐름에서의 변경 때문에 이 값이 변경되면서 하이젠버그를 낳을 수 있음
  • 이런 상황은 하이젠버그의 근본적인 문제. , 버그가 종종 특정 게임 기능과 관련 있는 것처럼 보이지만 실제로는 그렇지 않음. 예를 들면, 특정 박스를 열면 게임에 에러가 생기는 경우 개발자는 버그 원인처럼 보이는 곳(, 박스 열기와 관련된 코드)에 디버깅 노력을 집중하지만 진짜 문제는 전혀 다른 곳에 있을 수 있음. 이는 해당 버그를 올바른 프로그래머에게 할당하지 못하는 결과를 낳고, 프로그래머도 자신과는 상관없는 버그를 추적하느라 여러 날을 헛되게 낭비하는 일이 생길 수 있음
  • 초기화되지 않은 메모리 하이젠버그는 메모리를 알려진 값(메모리를 0으로 하는 것보다 문제를 야기할 가능성이 높은 값. , 0×55555555)으로 초기화함으로써 추적될 수 있음. 초기화되지 않은 변수는 컴파일러가 아예 허용하지 않도록 만들어서 미연에 방지할 수 있음(, C#에서처럼 언어적 결함이나 C++에서처럼 경고로 처리)

 

메모리 훼손(Memory Corruption)

  • 추적하기 가장 어려운 하이젠버그 타입 중 하나가 무작위의 메모리 훼손(, 임의의 빈도로 임의의 시점에 발생하며 메모리의 임의의 위치에 임의의 값이 쓰여진 버그)
  • 관련된 임의성(randomness)이 적을수록 디버거에게 좋음. , 특정 시점에 발생한다면 해당 시점에 정확히 무슨 일이 일어나고 있는지를 확인하고, 특정 위치에서 일어나면 write를 잡아채거나 또는 어떤 코드/데이터가 해당 위치로의 포인터를 가지는지 조사. 쓰여진 값이 항상 동일하면 이것도 단서가 됨. 예를 들어, 쓰여진 값이 항상 0x3fe80000이면(부동 소수점으로는 1.0f) 메모리에 1.0을 저장하는 것이 무엇인지를 체크
  • 무엇이 훼손되는지 얼마나 많이 훼손되는지 알기 어렵지만 만약 훼손 값을 볼 수 있다면 이 데이터를 단서로 훼손의 특징을 파악하는 노력을 할 수 있음. 예를 들면, 아래는 ASCII 데이터(파일명)hex 값 덤프를 보여줌

 

  • 위에서 hex 값만 보면 훼손이 있는지 즉각 알기 어렵지만 ASCII 데이터를 보면 어디가 잘못되었는지 바로 보임(둘 째 라인에 훼손이 있음). 실제 hex 값을 주의 깊게 보면 둘 째 라인의 첫 세 단어(words)가 다른 것들과 매우 다름(셋 중 두 개 단어는 hex 3으로 시작하는 부동 소수점 값처럼 보임). 따라서 위에 보여진 동일한 데이터를 아래처럼 float 모드로 전환하여 살펴보기로 함 

 

  • 이제 둘 째 라인의 첫 세 단어가 실제로 float 값임이 분명해짐(게임에서는 대부분의 float 값이 작다. 대개 1 미만). 그리고 자세히 보면 이것들이 단위벡터(unit vector)를 형성함을 알 수 있음. 어디서부터 훼손이 오는지 정확히 말해주지는 않아도 무엇인가가 메모리에 unit vector를 쓰고 있고 이 메모리 위치의 양쪽 이웃은 훼손시키지 않음을 알려줌(이런 단서가 디버깅에서 고려해야 할 대상을 좁혀준다)

 

 

반응형

+ Recent posts