Sep 19 2008

JNI 를 통해 사용하는 VC++ 라이브러리를 디버깅하기

분류: Lang.Java 태그: ,, , , , , Heart @ 4:21 오후

Trackback : http://dev.heartsavior.net/archives/205/trackback/

JNI를 사용하는 프로젝트 수행 중에 JNI 단독으로 테스트할 때는 일어나지 않는 오류가 계속 일어나길래, JNI 가 사용하는 VC++ 라이브러리 내부를 디버깅하는 방법을 찾아 보았다.

@ Java Native Interface (JNI) - Debugging C code in a dll

설명이 잘 되어있긴 한데, 정리는 좀 덜 되어 있어서 실제로 디버깅 테스트를 해 보고 절차를 정리해 보았다.

IDE는 Visual C++ 6 를 기준으로 설명한다. 다만, 설정해야 할 부분이 별로 없으므로 아래의 설정은 타 IDE에도 적용이 가능할 것 같다.

1. Project Settings 에서 JNI 구현이 되어 있는 프로젝트를 선택함
2. Settings For: 를 Debug로 맞춘 다음(혹은 Debug 옵션들이 선택된 Custom Configuration도 상관없음) Debug 탭 선택
3. Executable for debug session: JRE 의 java.exe 나 javaw.exe 를 선택
4. Working directory 에 JNI를 사용하는 프로젝트의 root 디렉토리를 전체 경로로 기록
(Eclipse 의 경우 debug / run 시에 해당 디렉토리를 Working directory 로 사용하므로 그에 맞춰주는 것임)
5. Program arguments 에
-Xrunjdwp:transport=dt_socket,server=y,address=<Eclipse 에서 원격 디버깅을 붙을 port>,suspend=n -classpath <jar파일들;class 디렉토리들(Eclipse라면 bin)> <Main 클래스 경로(패키지.클래스명)> <main() 의 arguments> 기록
(classpath 를 적을 때 Working directory 의 상대 경로로 적어도 된다.)

* 중요 포인트 : JNI 라이브러리를 로드하는 부분( System.loadLibrary() 나 System.load() ) 에서 IDE에서 빌드하는 결과 파일을 로드하도록 해 주어야 한다.
참고로, 라이브러리 경로를 절대 경로로 표현하려면 System.loadLibrary() 대신 System.load() 를 사용하여야 하고, 이 때는 경로에 확장자도 붙여 준다.

디버그할 때는 VC++ 에서 Configuration 을 위에서 설정한 대상(Debug) 으로 맞추고 F5를 눌러 실행하면 JVM이 실행된 Console 이 뜬다.  VC++ 에서 break 걸리면 VC++ 디버거 사용 방법대로 디버깅 하면 된다.

VC++ 6 에서 실행할 때 native method 를 호출하는 위치까지는 진행되어야 breakpoint라도 걸린다. (F11로 실행해 봐야 JVM Disassembly 가 뜨기 때문에 별 도움이 안됨.)

위와 같은 방법으로 JNI 를 통해 호출되는 라이브러리를 디버깅할 수 있다.

—-

다음으로, 양 쪽으로 모두 디버그 하는 방법을 알아보자. (Eclipse 3.3 기준)

VC++ 6 은 위와 설정이 같다.

Eclipse 에서는 아래와 같이 설정한다. (다른 IDE를 쓴다면 해당 IDE 가 원격 디버깅하는 방법대로 하면 됨)

1. Open Debug Dialog 를 실행한다.
2. Remote Java Application 을 선택하고 New 를 선택한다.
3. Main 탭에서 Project 를 선택하고, Connection Properties 에서 Port 를 VC++ 에서 설정한 port 를 적는다.

해당 Debug Configuration 을 실행하면 VC++ 가 실행한 JVM 에 원격 디버거로 붙게 된다.

주의해야 할 점은, 프로그램 로직이 짧으면 Eclipse 가 연결하려는 시도 중에 프로그램이 끝나버린다.
또한, VC++ 에서 break 가 걸린 상태에 Eclipse에서 연결을 시도하면, VC++ 에서 break 풀릴 때까지 Eclipse 는 연결이 안되는 것 같다. 즉, 연결에 대한 처리가 delay 되는 것 같다. (확실하지는 않음. 혹시 아시는 분은 리플로 알려주시면 감사하겠습니다.)

양 쪽으로 디버깅할 때에는, Java 프로그램이 시작할 때 native 의 초기 진입점(native method) 을 바로 실행하도록 하면 양쪽으로 디버그를 걸기가 좀 편하다.
초기 진입점에 breakpoint 걸고, VC++에서 멈춰져 있는 상태가 되면 Eclipse 에서 원격 디버거로 연결하고, VC++ 에서 바로 F5를 눌러 초기 진입점을 빠져나가면 (뭐 이리 복잡하냐 -_-) 이후로는 양쪽으로 breakpoint 에서 멈추게 된다.

어쨌든 절차도 복잡하고… 가장 좋은 방법은 JNI 로 사용할 라이브러리를 ‘디버깅할 일이 없도록’ 하는 것이겠다.

ps. 특정 IDE 에서 컴파일해야 하는 게 아니라면 Eclipse 의 CDE를 통해 Eclipse 내부에서 북치고 장구치는 것도 가능하다.

@ Integrated Debugger for Java*/JNI Environments