기적같은 디버깅. 컴파일러 버그? 역시나 그럴리가.

으아…일주일전쯤에 발견해서 날 미치게했던 그 버그를 잡았다.

총 쏠때 캐릭터가 사라졌다가 사격을 멈추면 다시 나타나는 버그.

VS2010의 v100컴파일러에서 VS2013의 v120컴파일러로 버전을 올리고 난 후 나타난 버그이고 반복된 테스트로 v120컴파일러로 컴파일한 바이너리에서만 나타는 버그라는 결론을 얻었었다.

릴리즈 풀 옵티마이즈 상태에서만 나온다.마침 VS2013은 Update 2 rc버전이다. 즉 VS2013의 v120컴파일러의 옵티마이저 쪽에 버그가 있다고 생각했다.

일단 그때도 Character.cpp의 OnShootingProcess()함수쪽이 옵티마이즈 되면 문제가 생긴다고는 추측했었다.

거기까진 좋았는데 컴파일러 버그라고 생각한건 역시 바보같았다.

사실 계속 찜찜하긴 했지. 풀옵티마이즈라고 해도 컴파일러 버그로 잘못된 코드를 생성하는 경우는 정말 흔치 않다.

게다가 십수개의 DLL을 이미 v120컴파일러로 컴파일했고 그놈들을 서버에서 사용하는데 3개 서버의 프로세스가 일주일동안 전혀 문제없이 돌고 있다.

이상하잖아.

정식 Update 2가 발표되면 확인해 볼 수 있겠지…라고 일단 고민을 좀 미뤄뒀었다.

오늘 아침에 기다리던 Visual Studio 2013 Update 2가 정식으로 릴리즈 됐다. 바로 설치하고 리빌드했다.

신의 도우심인지 그 잘 나오지도 않던 버그가 딱 처음 실행하자마자 나오더라.

거의 직감했다. ‘이거 컴파일러 버그 아니겠구나.’

VS2013 Update 2로 빌드해서도 동일한 문제가 발생하니 하루종일 찜찜해서 다른 일을 할 수 없었다. 그러고보니 몇 일 전 일이 생각났다.

몇 개월 전에 지금 개발중인 게임을 WinRT 기반으로도 포팅을 했었다.

Windows 8.0 타겟이었고 Visual Studio 2012의 v110컴파일러를 사용했었다. Update 2 rc버전이 나오기 전에는 VS2013에서 arm용 빌드를 하면 컴파일러가 무한 루프에 빠졌다. 그래서 계속 VS2012의 V110컴파일러를 사용해서 Windows 8.0타겟으로 빌드했었다.

그런데 몇 일전 누가 좀 보여달라기에 실행을 해보니 안돌더라. 다시 빌드를 하니 빌드가 안되더라. 할 수 없이 Windows 8.1타겟으로 하고 VS2013의 v120컴파일러로 싹 리빌드했다. Update 2 rc이후로 arm빌드 컴파일 문제는 해결되었으므로 무난히 8.1로 이전했다.

x86/x64/arm용으로 빌드를 만들었고 arm빌드를 서피스 RT에 올려서 작동을 확인했다.

그리고 그 다음날 자랑스럽게 보여줬는데 예의 그 버그가 튀어나왔다. 총 쏘면 캐릭터가 사라지는 버그. 그땐 그냥 ‘아 역시 컴파일러에 버그가 있어서 arm용 빌드에서도 같은 버그가 나타나는가보다.’라고 생각했다.

역시 어리석었다.

다시 오늘로 돌아와서. 그 서피스RT에서 재현됐던 버그를 떠올리고 나니 서피스RT에선 계속 재현이 가능할지도 모른다는 생각이 들었다.

서피스RT를 꺼내다가 테스트를 해봤다.

놀랍게도!!! 그 재현하기 어렵던 버그가 항상 나타난다.

WinRT용 게임을 서피스 프로와 PC에서 x86/x64로도 돌려봤으나 재현되지 않았다. 그런데 ARM용 릴리즈 빌드에선 언제나 재현이 됐다.

오오오오!!! 이제 잡을 수 있다!!!

좀 속도가 느려서 짜증나긴 하지만.

서피스 RT를 리모트 디버깅하면서 버그를 찾기 시작.

우선 가장 의심되는 Character.cpp코드를 처음부터 끝까지 #pragma optimize( “gpsy”,off),#pragma optimize( “gpsy”,on)로 감쌌다.

옵티마이즈 완전히 끄는거다.

역시 이렇게 하면 문제 안생김.

2분검색의 원리를 이용해서 절반씩 영역을 좁혀나갔다.

최종적으로 문제되는 함수 2개를 찾아냈다.

역시 최초에 추측했던대로 OnShootingProcess()와 OnShootingProcessGun()이네.

OnShootingProcessGun()하나만 옵티마이즈를 꺼놓고 보니 캐릭터가 사라지지는 않는데 사격할때 캐릭터가 90도 돌아간다. 어라?

코드를 살펴보니 사격시 히트포인트에 맞춰서 캐릭터 방향을 돌려주는 코드가 있다. 여기에 PI/2가 들어간다는건데. 허공에 쏴서 히트 포인트가 없을때도 PI/2가 들어가네. 그럴리가.

디버거로 확인해보니 히트 포인트의 Float3 변수에 비정상 float값이 들어가 있다.

으아!

뭔지 알았다.

히트 포인트를 저장하는 Flaot3변수를 초기화 하지 않은 것은 틀림없다. 코드 흐름상 초기화 하지 않은 상태로 각도를 구하는 함수로 전달되는 것도 맞다. 그런데 왜 디버그 모드에서 문제가 생기지 않았을까?

디버그 빌드에선 스택을 0xcccccccc로 채운 탓에 항상 일정한 값이 들어가있었고 이게 각도를 구하는 함수에 들어가면 거의 0에 가까운 값을 만들어냈던 것이다.

어차피 제대로 된 값은 아니었는데 0에 가까워서 몰랐지.

릴리즈에선 이 변수에 무슨 값이 들어갈지 알수 없고 실행할때마다 매번 다른 값이 들어가서 버그 재현이 힘들었던 것이다.

어떤땐 0에 가깝게 나오고 그렇지 않을땐 라디안값으로 쓸 수 없는 얼토당토한 값이 나와서 그 값으로 캐릭터 회전을 시키고 나면 캐릭터 사라짐 현상으로 연결되는 것이다.

이 초기화 되지 않은 float3값이 x86/x64에선 매번 다르게 채워졌고 ARM빌드에선 버그를 재현할 수 있는 꽤 일정한 값으로 채워졌었던 모양이다.

float3변수에 {0,0,0}을 넣는 것으로 버그는 수정됐고 릴리즈,디버그에 상관없이 완전히 정상작동하게 되었다.

어휴…

진짜 다행이다. 여러가지가 맞물려서 기적적으로 버그를 잡았다.

1. 초기에 캐릭터 사라짐 버그를 눈으로 확인하지 않았다면 서비스 시작할때까지도 버그를 잡을 수 없었을 것이다.

2. 오늘 아침 Visual Studio 2013 Update 2가 릴리즈되지 않았다면 여전히 컴파일러 버그라고 믿고 있었을지 모르겠다.

3. 서피스RT에서 돌리기 위해서 VS2013으로 빌드를 하지 않았다면 버그를 잡을 수 없었을 것이다.

4. 서피스RT의 arm빌드에서 버그가 재현되지 않았다면 버그를 잡을 수 없었을 것이다.

아아 이것은 신의 도우심인가.

돈은 못벌어도 버그 땜에 쪽팔리진 말라는…

우우 오늘은 발 뻗고 자겠다.


기적같은 디버깅. 컴파일러 버그? 역시나 그럴리가.”에 대한 답글 1개

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중