Sep 23 2008

간단한 문제…(연속된 숫자 그룹 맺어주기)

분류: DS/Algorithm 태그: ,Heart @ 11:25 오후

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

프로그래밍 갤러리에서 간단한 문제가 하나 올라오길래 오랜만에 C 코딩도 해볼 겸 풀어봤다.

문제 정의)
입력 : 1 2 3 10 11 12 13 19 199 200 201 202 300 305 499
출력 : 1-3, 10-13, 19, 199-202, 300, 305, 499.

연속된 숫자가 입력될 때 해당 숫자의 범위를 ‘-’ 로 이어주는 문제이다.
숫자 혹은 범위 간에는 ‘,’ 를 출력하고, 마지막 출력은 ‘.’ 이다.
입력 숫자는 1<=i<=500 이고, 입력 갯수는 1<=n<=500 이다.

내가 푼 답)

@ codepad 사이트에 입력한 코드

새로 들어오는 값이 마지막으로 들어온 값과 연속되는 값인지만 체크하면 문제가 될 만한 것은 없다.
나머지는 다 입출력의 문제…

#include <cstdio>
#include <cstdlib>
#include <cstring>
 
const char * DELIMETER = " ";
 
int main(void)
{
	char pszInput[] = "1 2 3 10 11 12 13 19 199 200 201 202 300 305 499";
 
	char * token = strtok(pszInput, DELIMETER);
 
	bool bFirstNumber = true;		// for check ',' print
	bool bContinue = false;			// for check continuous
	int nLastNumber = -10;			// Should not continue the first input
 
	while( NULL != token )
	{
		int nCurrentNumber = atoi(token);
 
		if( (nLastNumber + 1) == nCurrentNumber )
		{
			// continuous number
			bContinue = true;
		}
		else
		{
			if( bContinue )
			{
				// print last
				printf("-%d", nLastNumber);
 
				bContinue = false;
			}
 
			if( bFirstNumber )
			{
				printf("%d", nCurrentNumber);
				bFirstNumber = false;
			}
			else
				printf(",%d", nCurrentNumber);
 
		}
 
		nLastNumber = nCurrentNumber;
 
		token = strtok( NULL, DELIMETER );
	}
 
	printf(".");
 
	return 0;
}
</cstring></cstdlib></cstdio>

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


Sep 10 2008

CVS loginfo(post-commit trigger) 작업

분류: Tip.Tech 태그: ,, , Heart @ 11:45 오후

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

(프로젝트 중에 CVS 를 연동해야 하는 작업이 있어서, 당분간은 CVS 사용 관련 포스팅이 이어질 것 같다.)

CVS 의 저장소에 변경이 일어나는 시점에(commit) 특정 명령을 수행하게 할 수 있다.
자세한 설명은 아래의 post를 참조하고…

@ Letting CVS Pull The Trigger

여러 trigger 가 있지만, 그 중 commit 이후 수행되는 trigger 는 loginfo 이다.

trigger 를 수정하는 방법 중 가장 안전한 것은 저장소의 CVSROOT 모듈을 checkout 하고 수정한 다음 commit 하는 것이다.

포스트를 참고로 해서 loginfo 에 한 줄을 추가해 넣었다.

test/* (/cvs-log/postCommit.sh %p %{sVv} $USER)

test 모듈의 모든 파일을 대상으로 commit 발생 시 /cvs-log/postCommit.sh 를 실행하겠다는 뜻이다.
괄호 안의 내용들은 그대로 shell에서 실행되므로 실행될 argument 문자열을 입맛에 맞게 조절할 수 있다.

아래 인자들은 CVS에 의해 정보로 변환되어 들어간다.

%p : 디렉토리 경로(모듈 경로가 포함됨. ex. test/DirectoryA/)
%{sVv} : s 는 파일명, V 는 기존 버전 번호, v 는 신규 버전 번호
$USER : commit 한 유저 아이디

commit 시에 디렉토리 별로 shell 이 한 번 실행되는 것 같다. 예를 들면 commit 시에 변경 파일들이…

test/DirA/A.txt 1.1 -> 1.2
test/DirA/B.txt 신규(1.1)
test/DirB/C.txt 삭제(기존 1.3)
test/DirB/D.txt 1.3 -> 1.4

이라면 shell 은 이렇게 실행되었다.

/cvs-log/postCommit.sh test/DirA/ A.txt 1.1 1.2 B.txt NONE 1.1 cvsuser
/cvs-log/postCommit.sh test/DirB/ C.txt 1.3 NONE D.txt 1.3 1.4 cvsuser

예외 사항이 있다면, 디렉토리는 ADD 할 때 바로 hook 이 동작한다. 디렉토리 자체가 cvs에서는 commit 으로 추가되는 개체가 아니다.
그리고 파일과 구분하기 위해서, 디렉토리는 파일 명 부분에 ‘-’ 가 한 번 들어온다. (이후에 New, directory 가 더 들어오는데 알아서 걸러내면 된다.) 버전은 NONE NONE 으로 들어오니 이를 통해 구분해도 될 것으로 보인다.

CVS 는 디렉토리를 삭제할 수 없다. 저장소(물리적인 위치) 에서 직접 rm -rf 명령으로 삭제하여야 한다. 그러므로 디렉토리 삭제에 대한 감지는 되지 않는다.

postCommit.sh 자리에는 실행 가능한 어떠한 것이라도 올 수 있다. 여기서는 shell script 를 적었지만 그냥 막바로 프로그램을 돌릴 수도 있다. 링크된 post에서는 cvs 의 commit 이 block되기 때문에 처리가 많이 걸리는 것은 직접 걸어두지 말고 프로세스를 하나 더 두는 것을 제시하고 있다.

작업시 주의해야 할 것은, cvsd 설치 시에 chroot jail 설정을 한 경우에는 loginfo 가 실행하는 디렉토리 경로도 chroot가 적용된다는 것이다. (이것때문에 거의 반나절 가까이 보냈다.)
loginfo 가 실행해 주는 셸 내에서도 chroot가 적용되는 것으로 확인했다. (프로그램도 아마 마찬가지가 아닐까 생각된다.)

위의 예를 들자면, cvsd가 /var/lib/cvsd 에 설치되고 chroot 가 /var/lib/cvsd 로 설정되어 있다면, postCommit.sh 파일은 /var/lib/cvsd/cvs-log/ 에 위치해 있어야 한다.

그리고, sh(shell 실행파일) 이라던지 cat, date 등이 없기 때문에 필요하다면 chroot 의 해당 디렉토리로 복사해 넣어야 한다.

윈도우즈에서만 작업하다가 linux 작업을 하다 보니 chroot 에 대해 전혀 모르고 있다가 된통 당할 뻔했다.

ps. 작업환경은 우분투 OS 에 cvsd 패키지를 설치했고, CVS 버전은 1.12.13 (client/server) 이다.

ps2. received broken pipe signal 에러가 나서 한참 고생했을 때 chroot 문제임을 알게 해 준 포스트

@ cvsd: frequently asked questions


뒷 쪽 »