Altruistic Programmer's Blog (KR)

이타주의 프로그래머의 블로그

Archive for the ‘프로토콜버퍼’ tag

pdb를 공부해서 LNK4099 LNK4204를 해결하다

with 2 comments

여러가지 서드파티 라이브러리중에 유독 구글의 프로토콜버퍼만 LNK4099 LNK4204 경고를 무수하게 쏟아내고 있어서 pdb에 대해서 공부를 해봤다. 평소에 궁금해하던 vc90.pdb 파일의 정체를 알게 되어서 기쁘다.

2가지 종류의 pdb

pdb파일은 아래의 두 가지 종류가 존재한다.

  • 컴파일러가 생성하는 pdb
    • /Z7, /Zi, /ZI 등의 컴파일 옵션으로 임베딩 여부와 정보의 양을 결정
    • /Fd 컴파일 옵션으로 pdb 파일이 생성될 경로를 지정
    • VC2008이 지정하는 기본패스는 $(IntDir)\vc90.pdb
    • .obj 파일에 pdb 파일의 경로를 보관한다.
  • 링커가 생성하는 pdb
    • /DEBUB 옵션으로 생성여부 결정
    • /PDB 옵션으로 pdb 파일이 생성될 경로를 지정
    • VC2008이 지정하는 기본패스는 $(TargetDir)$(TargetName).pdb
    • .exe나 .dll 파일에 pdb 파일의 경로를 보관한다.

정적 라이브러리의 pdb

정적 라이브러리는 .obj 파일의 모음으로 볼 수 있으므로 vc90.pdb가 정적 라이브러리의 pdb 파일이 된다. 즉, .lib 파일을 배포할 때는 vc90.pdb도 함께 배포해야 한다.

/Z7은 디버깅 심볼 관련 옵션이기는 하지만 pdb랑은 상관없고 CodeView 포멧의 디버깅 심볼을 .obj에 임베딩시키는 옵션이다. 정적 라이브러리라면 .lib에 임베딩되는 것이고 별도의 .pdb 파일은 생성되지 않는다. 

정적 라이브러리에 기록된 pdb 파일의 경로 읽기

LNK4099나 LNK4204 경고가 났을때 정말로 pdb 파일이 없는지 확인하기 위해서 이 방법을 사용할 수 있다. 단계를 요약해보면

  1. .lib 파일로부터 .obj 파일을 추출한다
    • lib /extract:myobj.obj mylib.lib
  2. .obj 파일에 기록된 .pdb 파일의 경로를 확인한다
    • dumpbin /section:.debug$T /rawdata myobj.obj

주의할 것은 myobj.obj는 보통 ./Debug/myobj.obj처럼 된다는 것. lib 파일을 메모장등에서 열어보면 확인할 수 있다.

LNK4099, LNK4204의 원인은?

정말로 vc90.pdb가 없거나 vc90.pdb는 있는데 그 안에 디버그 정보가 없다는 것. 이번 경우에는 프로토콜버퍼의 3가지 프로젝트가 똑같은 곳에다 vc90.pdb를 생성하는 바람에 파일이 덮어써졌다. 그래서 파일은 있지만 디버그 정보가 없는 경우에 해당한다.

구글의 C++ 테스트 프레임웍의 경우처럼 intermidiate directory(기본은 debug, release)에 프로젝트 이름을 다시 추가하는 방식으로 설정을 바꿔주면 깔끔하게 해결할 수 있다.

  • Output Directory : $(ConfigurationName)
  • Intermediate Directory : $(OutDir)\$(ProjectName)  

교훈

이 날 배운 교훈은 역시나 워닝 메시지는 친절하지 않다는 것이다. 유저인터랙션 분야에서 사용자가 어떻게 제품을 사용하는지 비디오로 녹화해서 연구하는 것과 같은 사용성 분석을 VC개발팀이 하고 있는지 궁금하다.

LNK4099의 에러메시지는 아래와 같은 포멧이다.

libprotobufd.lib(common.obj) : warning LNK4099: PDB 'vc90.pdb' was not found with 'c:\xxx\protobuf-2.2.0\vsprojects\Debug\libprotobufd.lib' or 'c:\yyy\bin\vc90.pdb';linking object as if no debug info

우선 실제로 vc90.pdb 파일이 존재하는 경로는 에러메시지에 나오지 않는다. .lib를 분석해보면 vc90.pdb의 파일 경로는 아주 온전하게 저장되어 있지만, 에러메시지에 나오지 않을 뿐이다.

c:\yyy\bin\은 라이브러리를 링크하는 exe 파일이 생성되는 위치다. 거기서 vc90.pdb를 찾고 있다는 얘기다.

그러고는 vc90.pdb 파일을 찾을 수 없다고 한다. -_-;;

링크

http://www.debuginfo.com/articles/gendebuginfo.html 
좀 옛날 것이지만 디버그 심볼에 대해서 자세하고 친절하게 설명해준 훌륭한 글


Written by muscly

April 18th, 2010 at 4:36 pm

Protocol Buffer

with 6 comments

[옛날 블로그 글입니다. 2009.07.02]
C++로 네트웍 프로그램을 만드려면 패킷을 정의하고 읽고 쓰는 유틸리티가 필요하지요. 그냥 구조체로 하려고 하면 문자열 같은 것 처리하기가 너무 힘들더라구요. 회사에도 그런 라이브러리가 하나 있기는 한데, 아쉬운 부분이 있어서 좀 찾아봤더니 구글에서 프로토콜 버퍼라는 녀석을 만들어 뒀더라구요. 패킷의 페이로드를 .proto 파일에 정의하고 protoc.exe로 빌드하면  패킷을 읽고 쓰기 쉽도록 코드를 생성해줍니다. c++, java, python을 지원하구요, 네트웍 패킷에 한정된 건 아니고 파일 입출력에도 사용할 수 있는 범용적인 프로젝트입니다. 그래서, 자동으로 패킷 아이디를 생성해준다거나 하는 기능은 없습니다. 아래 링크 보시면 심플하게 설명 잘 해놨습니다. http://code.google.com/p/protobuf/ 프로토콜 버퍼에서 제일 맘에 드는 부분은 패킷을 입출력하는 스트림을 직접 만들어서 넣을 수 있다는 점인데요. 기본적으로 배열, iostream, 파일 등을 기반으로 하는 스트림도 구현해 놓아서, 그냥 이것들을 사용해도 됩니다. 스트림 자체가 zero-copy 컨셉이라 패킷 입출력시에 메모리 복사가 적은 장점이 있구요, 혹시나 어플리케이션에서 자체적으로 사용하는 스트림 클래스가 있다면 어댑터를 만들어 주는 것만으로 프로토콜 버퍼와 함께 사용할 수 있으니 불필요한 메모리 복사를 줄일 수가 있습니다.

마무리

프로토콜 버퍼는 기능도 좋지만 오픈 소스라서 좋네요. 소스 코드를 고치고 할 일은 없겠지만, 오픈 소스라서 누구나 맘 편하게 가져다 쓸 수 있고, 또 그래서 부족한 부분을 채워 줄 파생 프로젝트들이 생겨나니까 좋습니다. 그리고 오픈 소스라서 생명력이 길어지는 점도 있는 것 같구요. 여러 모로 좋네요, 오픈 소스는.

스트림 클래스 분석

여기서부터는 더 깊숙한 얘기라 관심있는 분만 보시는 게 좋겠네요. 이번 프로젝트에 프로토콜 버퍼를 써볼까 하는 생각에 스트림 클래스의 구조를 공부해봤습니다. 스트림은 패턴의 예제로서 많이 나오잖아요, 그런 예제같이 모범적인 구조를 가지고 있습니다. 우선 ZeroCopyInputStream과 ZeroCopyOutputStream이 입출력 스트림의 인터페이스 클래스입니다. 이를 구현하는 클래스가 여럿 있는데요, 배열, 파일, iostream 등을 이용한 구현등이 있습니다.  아래가 그 클래스 다이어그램입니다.

입력스트림 계층 구조

출력스트림 계층 구조

그 다음에 CodedInputStream, CodecOutputStream은 정수나 문자열, 로우 버퍼를 스트림에 넣고 쓰는 스트림 클래스입니다. 데이타를 효율적으로 읽고 쓰기위한 엔코딩 방식을 담당하는 녀석이지요. 내부적으로 ZeroCopyInputStream, ZeroCopyOutputStream을 사용합니다.

Written by muscly

July 2nd, 2009 at 3:10 pm