윈도우즈에서 디버깅을 할 때, 깨끗한 콜스택을 얻기 위해서는 심볼 관리가 필수다. 이 심볼 관리에 들어가는 노력을 덜어주는 심볼 서버의 기능에 대해서 정리해 보고자 한다.
Debugging Applications for Microsoft .NET and Microsoft Windows에 나온 내용을 나름대로 정리한 것이다.
MiniDump를 이용해서, 덤프 파일을 생성하는 기능을 애플리케이션에다 추가했다고 하자.
OS 심볼을 다운로드받아 설치해 두었다면, 한 컴퓨터에서 개발하고, 테스트하는 동안은 큰 문제가 없을 것이다. 각종 바이너리들의 버전이 같기 때문이다. (같은 컴퓨터니까!) 하지만 개발은 2000에서 하고, 실행은 XP에서 하게 되는 경우를 생각해 보자. 이 경우 커널 DLL 등이 전혀 틀리기 때문에, 덤프 파일을 받아와서 봐도 콜스택이 제대로 나오지 않는다. 그렇다고 일일이 해당하는 바이너리와 심볼 파일들을 찾아서 세팅해 주는 것도 거의 불가능하다.
그렇다면 도대체 어떻게 해야하나? 마이크로소프트는 이 문제를 해결하기 위해서 심볼 서버라는 기능을 도입했다. 심볼 서버의 기능을 요약하자면, “덤프 파일이 생길 때의 환경을 재현하기 위해서 필요한 심볼들을 자동으로 다운로드” 정도가 되겠다.
즉 위에서 말한 “2000에서 개발, XP에서 테스트”의 경우, XP에서 생긴 덤프 파일을 받아와서 2000 디버거에서 띄우면 XP 쪽의 심볼을 다운로드받아서 로드한다는 말이다. 너무 좋은 기능 같아 보인다! 그러나 이것저것 해줘야할 일들이 꽤 많다.
일단 심볼 서버로 사용할 컴퓨터를 하나 마련한다. 하드만 넉넉하면 된다. 각각의 컴퓨터에다가 심볼 서버를 구성하는 것도 가능하지만, 하드 낭비, 대역폭 낭비다. 하지만 어쩔 수 없다면 자기 컴퓨터에 해도 상관없다. 어쨌든 이 컴퓨터의 이름을 SSERVER라고 하자.
SSERVER 컴퓨터에다가 공유 디렉토리를 두 개 생성한다. 하나는 OS 심볼, 하나는 개발 중인 애플리케이션의 심볼을 저장할 곳이다. 전자는 SYMBOLS_OS, 후자는 SYMBOLS_PRODUCT라고 하자. 모든 프로그래머들이 접근 가능하도록 권한을 설정해 준다.
프로그래머가 작업하는 각각의 컴퓨터에 Debugging Tools for Windows를 설치한다. 그 다음 디버깅 툴을 설치한 디렉토리를 패스에다 추가해준다. symsrv.dll 파일을 어디서든지 액세스할 수 있도록 만들기 위해서이다. 그리고 환경 변수 설정하는 부분에 가서 _NT_SYMBOL_PATH 변수의 값을 아래와 같이 설정한다. (없다면 만든다.)
SRV*\\SSERVER\SYMBOLS_OS*http://msdl.microsoft.com/download/symbols;SRV*\\SSERVER\SYMBOLS_PRODUCT
여기까지만 해도 OS 심볼 파일들을 디버거가 다운로드받는 것을 볼 수 있을 것이다. 그러나 아직 완벽하지 않다.
덤프 파일을 가지고 디버깅을 하기 위해서는, 필요한 것들은 다음과 같다.
당연히 네가지 모두 덤프 파일이 생긴 컴퓨터(애플리케이션이 다운된 컴퓨터!)의 것들을 말한다. 위에서도 언급했듯이 _NT_SYMBOL_PATH와 디버거를 이용하면 OS 심볼 파일은 해결할 수 있다. 그러나 OS 바이너리 자체는 다운로드받을 수 없다. 즉 뭔가 다른 방법이 필요하다는 말이다.
이 문제를 위해서 Debugging Tools for Windows에 딸려오는 심볼 체커(Symbol Checker, symchk.exe)와 심볼 스토어(Symbol Store, symstore.exe)를 이용할 수 있다.
심볼 체커는 커맨드 라인 프로그램으로서, 마이크로소프트로부터 OS 심볼을 다운로드하는 역할을 담당한다. 기본적인 사용법은 대강 다음과 같다.
symchk /r C:\WINNT\SYSTEM32 /s SRV*\\SSERVER\SYMBOLS_OS*http://msdl.microsoft.com/download/symbols
SYSTEM32 디렉토리 하위에 있는 모든 바이너리들에 대한 심볼 파일을 다운로드받는다.
심볼 스토어는 임의의 심볼 또는 바이너리를 사용자의 심볼 저장소에다 집어넣는 역할을 담당한다. 기본적인 사용법은 대강 다음과 같다.
symstore add /r /f D:\build\*.* /s \\SSERVER\SYMBOLS_PRODUCT /t "MyApp" /v "Build 632" /c "2004/05/28 Daily Build"
D:\build 디렉토리 및 하위 디렉토리에 존재하는 모든 심볼 파일과 바이너리 파일을 심볼 저장소에다 저장한다.
위의 예에서도 나와있듯이 심볼 스토어를 이용하면, 사용자의 바이너리와 심볼 파일 또한 저장해둘 수 있다. 버전 별로 바이너리와 심볼을 저장해 두면, 막상 덤프 파일이 생겼을 때, 일치하는 바이너리와 심볼 파일을 찾아 헤메지 않아도 된다.
그렇다면 심볼 체커와 심볼 스토어로 무엇을 해야하겠는가? 가만히 생각해 보면 알 수 있을 것이다. 말로 표현하기가 좀 그러니 슈도 코드로 표현해 보겠다.
// 지원해야할 각각의 OS마다, // 서비스팩과 핫픽스를 점진적으로 설치해가면서, // 심볼 체커를 이용해 심볼 파일을 저장소에다 받고, // 심볼 스토어를 이용해 OS 바이너리를 저장소에다 저장해야한다. for each OS in 지원해야할 OS { for each 서비스팩, 핫픽스 in OS { 심볼 체커를 이용해 심볼 파일을 저장소에다 저장; 심볼 스토어를 이용해 OS 바이너리를 저장소에다 저장; } }
딱 봐도 상당히 고통스러운 일이다. 누가 대신 좀 안 해주나… 서버라면 그나마 나은데, 클라이언트의 경우에는 98, Me, 2000, XP 다 설치해가며 심볼 받아둬야 할 테니…
험한 여정을 거쳐, 모든 바이너리와 심볼을 다 설치했다고 하더라도, 일치하는 소스 파일이 없으면 말짱 도루묵이다. 심볼 파일에다가 소스 파일을 집어넣을 수 있는 기능이 있다면 좋겠다만 그런 방법은 없다. 차선책으로 스크립트를 이용해 작업에 들어가는 수고를 약간이라도 덜어야 생명 연장에 도움이 된다.
그랬었는데, SourceServer라는 기능이 생겼다.