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


Jun 18 2008

eclipse 가 내장 컴파일러가 있었구나… 게다가 이런 만행(?!)을…!!

분류: Lang.Java 태그: ,, , Heart @ 12:09 오전

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

탐구의 시작은 디씨 프갤의 삼각네거리 님의 한 포스트에서 시작되었다.

@ 병맛 이클립스

interface AAA {
	void ccc();
	void ddd();
}
 
abstract class A implements AAA {
	public abstract void ccc();
	public abstract void ddd();
}
 
class B extends A {
	public void ccc() {
		System.out.println("Success");
	}
}
 
public class Test {
	public static void main(String ar[]) {
		AAA a = new B();
		a.ccc();
		//a.ddd();
 	}
}

그냥 딱 봐도 class B에 오류가 있음을 알 수 있는 코드이다. 당연히 eclipse 에서도 에러 표시가 된다.

근데, 실행이 된다. 에러를 표시함에도 불구하고 강제 실행이 가능한 것이다.
게다가, 아무런 오류도 없이 Success 가 출력이 된다.

뭔가 이상하다 싶어서 콘솔에서 직접 javac를 통해 컴파일해 보았다.
하지만 javac를 통해 컴파일하는 경우 B.class와 Test.class는 만들어지지 않았다. -g를 해도 마찬가지였다.
class 파일이 생기지 않으니 당연히 실행할 수 있는 방법이 없다.

일단 ‘eclipse 내에 컴파일러가 있겠구나’ 라는 것은 짐작할 수 있었고(최종 확인은 구글신을 이용;;) 어떻게 구현되어 있는지 보기 위해 eclipse가 만든 B.class 를 열어보기로 했다.

에디터로 열어본 B.class 는 무언가 이상했다

ddd 특문~ java/lang/Error 특문~ ! 특문~ Unresolved compilation problem:
The type B must implement the inherited abstract method A.ddd()

디컴파일을 하진 않았으니 추측이긴 하지만, bytecode도 텍스트 부분만 보면 함수 프로토타입이라던지 문자열 등은 얼추 보인다. ddd가 있다는 건, 내가 구현하지 않았지만 eclipse JDT 컴파일러가 ‘직접’ 구현해서 넣었다는 이야기이리라.

게다가 ddd()는 파라미터가 없는데, java/lang/Error가 있다. throw의 향기가 난다.
ddd()가 구현되어 있다고 생각하면, 테스트할 수 있는 방법은 간단하다.
위의 코드에서 a.ccc();를 주석처리하고 a.ddd(); 의 주석을 해제한 다음 eclipse에서 강제로 실행하면 ‘예상한 대로’ 빨간색으로

Unresolved compilation problem:
The type B must implement the inherited abstract method A.ddd()

라고 나온다.
(최종 확인은 디씨 프갤 그런데 님의 디컴파일로 확인할 수 있었고, 역시나 throw java.lang.Error() 로 구현되어 있었다. B.class 의 전체 디컴파일 결과는 ps2를 참조…)

‘컴파일 실패’ 하는 코드에 컴파일러가 관여해서 ‘실행 가능한 코드’로 만드는 데는 무언가 원인이라던지, 철학이 있을 것 같은데… 나로써는 단순히 추측할 뿐이었다.
추측하던 도중에, 그런데 님의 포스트에 유동닉이 남겨준 ‘클래스 부분(수정분) 컴파일로 컴파일 속도 극대화’ 라는 리플이 있어 구글링을 살짝 했더니 실제로 eclipse의 JDT Core Component 페이지

An incremental Java compiler. Implemented as an Eclipse builder, it is based on technology evolved from VisualAge for Java compiler. In particular, it allows to run and debug code which still contains unresolved errors.

와 같은 내용이 있다.

unresolved error 가 있는 상태에서 run과 debug 가 가능하게 하는 컴파일링… 이라…
error가 있으면 output이 나오지 않는 Sun compiler, error가 있어도 알아서 처리해서 output을 내놓는 Eclipse compiler.
어느 쪽이 옳은 걸까? 실수 방지의 측면을 더 고려해야 할까? 효율의 측면을 더 고려해야 할까?

요즘 회사 일이 새로운 게 없어서 많이 지쳐있었는데, 오랜만에 흥미 있는 주제를 탐구한 것 같다. :)

ps. 탐구한 내용들을 머릿속에서 재구성해서 썼는데, 재구성 전에 좀 더 편하게 쓴 버전은 디씨 프갤에 작성하였다.

@ ‘병맛 이클립스’ 에 대한 나름대로의 고찰(뻘글주의)

ps2. 참고자료 - eclipse 가 만든 B.class를 디컴파일한 소스

그런데 님이 위의 포스트를 보고 eclipse가 만든 B.class를 디컴파일 한 소스를 올려 주었다. (감사!!)

@ 아래 이클립스 컴파일러의 만행


Mar 25 2008

MS-Eclipse, 윈도우 환경에 자바 탑재 협력

분류: Dev.Info 태그: ,, , , , , Heart @ 10:15 오전

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

@ MS-이클립스, 윈도우 환경에 자바 탑재 협력

요약하자면… MS 측에서 “SWT에서 WPF를 사용하도록 Eclipse 측에 지원을 해 주겠다” 라고 했다는 이야기.

SWT 내부 구조는 모르고 있던 터라 찾아봤는데, OS별 Graphic Toolkit 혹은 API 에 접근해서 그래픽을 구현하고 있다.
기존 Graphic Toolkit이나 API들은 보통은 native(C/C++)로 되어 있고, 이를 JNI를 통해 접근하는 방법이었다.

그렇다면, MS에서 SWT에 WPF를 사용하도록 지원을 해 준다면, JNI를 통해 WPF를 접근하는 방법으로 지원해 주는 것일까?
그냥 Win32 API -> WPF 로 지원 플랫폼 추가만 되는 것이라면, 별반 큰 의미는 없지 않을까 생각된다.
WPF로 만든 App.를 아직 못 본 탓일까?
가뜩이나 native보다는 리소스 많이 먹을 수 밖에 없는데, JVM에 WPF(.net Framework) 까지 같이 돌아가야 된다면 모르긴 몰라도 리소스를 꽤 잡아먹을 것 같은 느낌이 든다.

음… 게다가 우리나라에는 해당사항이 그다지 없는 것 같기도 하다.
윈도우즈 프로그래밍에 자바를 활용하는 게 일반적인 모습은 아니니까…