dh_0e

[Bug] 디버깅이 문제를 해결하는 하이젠버그(Heisenbug) 본문

Bug

[Bug] 디버깅이 문제를 해결하는 하이젠버그(Heisenbug)

dh_0e 2024. 7. 25. 21:58

 최종 프로젝트로 서버를 만들어 캠프에서 제공된 클라이언트에  연결하는 과정을 진행하다 ProtoBuf 패킷 통신 중에 에러가 발생했다. 평범한 버그가 아니라 확률 게임처럼 10번 시도에 5~7번 꼴로 버그 발생..

OnRecvCompleted Failed Google.Protobuf.InvalidProtocolBufferException: While parsing a protocol message, the input ended unexpectedly in the middle of a field.  This could mean either that the input has been truncated or that an embedded message misreported its own length.
  at Google.Protobuf.SegmentedBufferHelper.RefillFromCodedInputStream (System.ReadOnlySpan`1[System.Byte]& buffer, Google.Protobuf.ParserInternalState& state, System.Boolean mustSucceed) [0x00091] in <fe213bb7ec1944e78688ae4e7c4ccb6d>:0 
  at Google.Protobuf.SegmentedBufferHelper.RefillBuffer (System.ReadOnlySpan`1[System.Byte]& buffer, Google.Protobuf.ParserInternalState& state, System.Boolean mustSucceed) [0x00008] in <fe213bb7ec1944e78688ae4e7c4ccb6d>:0 
  at Google.Protobuf.ParsingPrimitives.ReadRawBytesIntoSpan (System.ReadOnlySpan`1[System.Byte]& buffer, Google.Protobuf.ParserInternalState& state, System.Int32 length, System.Span`1[T] byteSpan) [0x00013] in <fe213bb7ec1944e78688ae4e7c4ccb6d>:0 
  at Google.Protobuf.ParsingPrimitives.ReadRawBytesSlow (System.ReadOnlySpan`1[System.Byte]& buffer, Google.Protobuf.ParserInternalState& state, System.Int32 size) [0x0003f] in <fe213bb7ec1944e78688ae4e7c4ccb6d>:0 
  at Google.Protobuf.ParsingPrimitives.ReadRawBytes (System.ReadOnlySpan`1[System.Byte]& buffer, Google.Protobuf.ParserInternalState& state, System.Int32 size) [0x00049] in <fe213bb7ec1944e78688ae4e7c4ccb6d>:0

 

서버가 패킷을 정확히 보내고 있는지 콘솔 로그를 찍어 디버깅을 해보려는데 엄.. 버그가 사라졌다.

콘솔 로그를 지우니 다시 확률적으로 생기는 버그님 ^^

나다 이 XXX야

하이젠버그(Heisenbug)

  • 양자 물리학 개념인 "하이젠베르크의 불확실성 원리"에서 유래한 하이젠베르크 + 버그의 합성어
    • 양자역학의 자세히 조사하려 하면 사라지거나 성질이 변하는 부분이 비슷함
  • 프로그래밍에서 테스트를 수행할 때 발생되는 버그의 형태 중 하나로 문제를 해결하기 위해 디버깅을 수행하면 사라지거나 성질이 변하는 컴퓨터 버그
  • 특정 조건이나 환경에서만 발생하고, 다른 환경에서는 발생하지 않거나 그 횟수가 적을 수도 있다.
    • 프로젝트를 진행하는 팀원들 마다 에러 발생 확률이 달랐다.
      • (팀원 A 10%, 필자 60%, 팀원 B 70%, 팀원 D 100% ... )
    • 이는 타이밍, 메모리 배치, 스레드 동기화 등의 문제로 발생한다.
  • 동일한 입력과 조건에도 매번 동일하게 발생하지 않으며, 가끔씩 또는 무작위로 발생한다.
    • 메모리 할당 / 해제 문제나 포인터 오류 등 환경 문제
  • 주로 멀티스레드 프로그램이나 비동기 처리가 된 프로그램에서 발생하며 디버그를 위해 로그를 찍어보는 행위가 영향을 주어 발생하지 않기 때문에 디버깅이 어려워짐

 

해결 방법

로그 및 프로파일링 도구 적절히 사용

  • 문제의 발생 빈도와 조건을 좁히기 위해 로그를 사용하되, 로그가 버그의 증상을 변경하지 않도록 주의해서 사용해야 한다.

다양한 환경에서 테스트

  • 가상 머신이나 컨테이너, 타 PC 등을 활용하여 다양한 시스템 환경에서 테스트
  • 문제가 발생할 수 있는 다양한 환경에서 테스트하여 문제를 재현할 가능성을 높임

재현 가능한 테스트 케이스 작성

  • 문제가 발생할 수 있는 상황을 모방하여 재현 가능한 테스트 케이스를 작성
  • 이 또한 테스트 케이스와 관련 없는 하이젠버그면 소용 X

 

필자는 며칠을 팀원들과 고민하다 튜터님께 자문을 구하여 해결했다..

튜터님이 캠프에서 제공된 클라이언트 코드를 해석해 보신 결과 프로토타입 버퍼를 수신하는 C# 코드가 비동기로 처리가 되어있는데, 패킷의 수신 결과 처리가 끝날 때 패킷을 비워주는 부분에 의해 발생했다.

비동기로 패킷을 처리하는 과정에선 수신 결과 처리가 끝나기 전에 다른 패킷이 들어올 수 있고, 패킷이 초기화되지 않은 상태에서 다음 패킷을 덧붙여버려서 패킷이 제대로 수신되지 않았던 문제였다.

(다행히 서버 문제는 아니었던 걸로..)

클라이언트의 패킷 처리 부분을 동기로 수정하거나 패킷을 받기 전에 초기화부터 하게 코드를 수정하면 해결될 것 같다.