2007/04/18

Dtrace와 Java : Java를 Dtrace로 해부해볼 수 있을까?

어려운 문제다.
Dtrace를 만든 3명중에 한명인 Adam 조차도 Java를 dtrace로 완전히 분해하는 게
어렵다고 했다. 그나마 JDK 1.6이후에는 솔라리스10의 jvm이 dtrace probe를 지원하게
되서 훨씬 쉬어지긴 했지만 말이다.

JDK 1.6.x이전에는 java vm은 운영체제 입장에서 독립적인 프로세스이면서, 내부적으로는 자바 애플리케이션을 실행시켜주는 머신 역할을 하고 있어서, 실제로 jvm의 애플리케이션인 java의 thread들은 운영체제에서 정확하게 무슨일을 하는지 알 수가 없었다.  그래서, jvm을 블랙박스라고 했다.

안에  어떤일이 일어나는 지 모르는 상태에서 jvm의 그 자체에 대고 dtrace 때려봐야 코끼리 뒷다리 만지기식이었다고 할까.

그래서, JDK 1.4.x와 JDK 1.5.x에서는 jvm이 구동될때 dtrace로 추적이 가능하도록 하는 플러그인 모듈(JNI 인터페이스)이 오픈 소스 프로젝트로 개발되었는데 성능이 워낙  후지다 보니, 욕을 많이 먹는 와중에 jdk 1.6.x이 발표되면 흐지 부지 되었다.

기술적으로는 운영체제에서는 jvm 안에 있는 thread의 움직임을 알수가 없으므로 jvm이 내부의 움직임을 외부 운영체제에 알려주어야만 운영체제에서는 무언가를 해볼 수가 있다. 그나마, 솔라리스8에서 솔10이 되면서 Thread-to-LWP 할당 방식이 1-to-1이 되었기 때문에 Java Thread를 운영체제에서 어느 정도 알게 된것이 발전이라면 발전이다. (참고 pstack, jstack)

JDK 1.5이전에는 java vm이 dtrace에게 java application에 대한 정보를 제공해주기 위해서 java vm이 제공하는 JNI기반의 DVMTI라는 trace interface module을 통하여 dtrace를  지원했었다. (https://solaris10-dtrace-vm-agents.dev.java.net/ )

그러나, 이 모듈은 매우 심각한 오버헤드로 많은 자바 개발자들로 하여금 경악을 금치 못하게함으로써 많은 사용이 되지 않았으며, 그러는 동안에 jdk 1.6 버젼에서 외부 trace를 위한 기능이 대폭적으로 개선되면서, 솔라리스용 java vm 1.6.x는 특별히 자체적으로(natively) dtrace를 지원하도록 개선되었다.

쉽게 얘기하면, jdk 1.6대를 사용해서 무언가를 돌리고 있으면, 애플리케이션은 자동적으로 java vm의 hotspot vm의 dtrace probe(검사 포인트)를 실시간 등록을 해준다. 때문에, java vm 로딩은 다소 좀 늦어지긴 했으나, 모니터링 부분에서는 혁신적인 발전이 제공되었다고 볼 수 있다.

아울러 jdk 1.6이 제공하는 trace 확장 기능은 java vm 자체의 모니터링 및 trace 기능도 발전시켜서 아주 화려한 툴들을(jconsole, demo/JTop, demo/MemoryManagement) 기본적으로 제공함에 따라 dtrace의 필요성이 많이 감소해졌다. ㅡ.ㅡ;

jconsole


그러나, 여전히 dtrace는 막강하다. 애플리케이션의 문제를 논리적으로 접근하는 힘은 어디에서나 강할뿐 아니라, jdk 1.6이 java thread의 procedure에 대한 정보를 모두 노출함으로써 dtrace적 논리로 문제의 근원을 찾아볼 수 있게 되었다.

freemind라는 java application을 jdk 1.6으로 실행했을 경우이다.
bash-3.00# pgrep freemind
2352
bash-3.00# ptree 2352
2352  /bin/bash /usr/sfw/bin/freemind.sh ; freemind를 실행시켜 주는 쉘이다.
  2356  /usr/bin/java -Dfreemind.base.dir=/usr/sfw/lib/freemind-bin-max-0.8.0/ ; 실제 freemind.jar의 java pid
bash-3.00# dtrace -l | grep 2356 | c++filt | tail
62071 hotspot_jni2356        libjvm.so jni_UnregisterNatives UnregisterNatives-entry
62072 hotspot_jni2356        libjvm.so jni_UnregisterNatives UnregisterNatives-return
62073 hs_private2356         libjvm.so void VM_CMS_Initial_Mark_Operation::doit() cms-initmark-begin
62074 hs_private2356         libjvm.so void VM_CMS_Initial_Mark_Operation::doit() cms-initmark-end
62075 hs_private2356         libjvm.so void VM_CMS_Final_Remark_Operation::doit() cms-remark-begin
62076 hs_private2356         libjvm.so void VM_CMS_Final_Remark_Operation::doit() cms-remark-end
62077 hs_private2356         libjvm.so HashtableEntry*Hashtable::new_entry(unsigned,oopDesc*) hashtable-new_entry
62078 hs_private2356         libjvm.so void RuntimeService::record_safepoint_begin() safepoint-begin
62079 hs_private2356         libjvm.so void RuntimeService::record_safepoint_end() safepoint-end
62356 hotspot_jni2925        libjvm.so jni_GetCharArrayRegion GetCharArrayRegion-return


(  c++filt는 Sunstudio를 설치해야만 제공되는 C++ name demangle 유틸이다./opt/SUNWspro/bin에 있다.  )

java가 pid 2356의 dtrace probe를 위한 probe point를 대거 등록해놓은 것이 보인다.
이중에서 hotspot_jni2356으로 등록되어 있는 probe statement가 중요하다. 실제 자바 애플리케이션의 함수들을 trace할때 필요하다.

그러면, freemind라는 java application을 dtrace할 수 있게 되었다. dtrace 해주자. ;-)
#dtrace -n 'hotspot_jni2925:::{@[probefunc]=count()}' ; pid 2925가 가장 일을 많이 하는 함수를 프린트하자.
...
  jni_IsAssignableFrom                                           1984
  jni_GetSuperclass                                              2388
  jni_IsInstanceOf                                               3818
  jni_SetIntArrayRegion                                         27688
  jni_GetByteArrayElements                                      28064
  jni_MonitorEnter                                              28064
  jni_MonitorExit                                               28064
  jni_ReleaseByteArrayElements                                  28064
  jni_GetArrayLength                                            36036
  jni_GetPrimitiveArrayCritical                                 38248
  jni_ReleasePrimitiveArrayCritical                             38248
  jni_CallStaticVoidMethod                                      69000


이렇게 나온다. 햐! 엄청 좋아졌다.
그러면, 가장 많은 부하를 제공하고 있는
jni_CallStaticVoidMethod()가 호출되는 어떤 경우일까? 또 dtrace해주자 !

#dtrace -n 'hotspot_jni2925::jni_CallStaticVoidMethod:CallStaticVoidMethod-entry{@[jstack()]=count()}'
몇가지 경우가 나타났다.

...
              libjvm.so`jni_CallStaticVoidMethod+0xe0
              libmawt.so`X11SD_Lock+0x43
              libawt.so`Java_sun_java2d_loops_MaskFill_MaskFill+0x96
              sun/java2d/loops/MaskFill.MaskFill(Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Ljava/awt/Composite;IIII[BII)V*
              sun/java2d/pipe/AlphaColorPipe.renderPathTile(Ljava/lang/Object;[BIIIIII)V*
              sun/java2d/pipe/DuctusShapeRenderer.renderPath(Lsun/java2d/SunGraphics2D;Ljava/awt/Shape;Ljava/awt/BasicStroke;)V*
              sun/java2d/pipe/DuctusShapeRenderer.fill(Lsun/java2d/SunGraphics2D;Ljava/awt/Shape;)V*
              sun/java2d/pipe/PixelToShapeConverter.fillRect(Lsun/java2d/SunGraphics2D;IIII)V*
              sun/java2d/S
              0xfc6a5f35
              0xfc402e9d
              0xfc6dc99d
              0xfc402e9d
              0xfc402e9d
              0xfc758abc
              0xfc76f8fa
              0xfc402c71
              0xfc402c71
              0xfc6947fe
              0xfc782015
              0xfc75cc9c
              0xfc402e9d
              0xfc402e9d
              0xfc402e9d
              0xfc768b95
              0xfc699bb4
              0xfc5cd0d0
              0xfc402c71
              0xfc402e9d
              0xfc402e9d
              0xfc402e9d
              0xfc402e9d
              0xfc400202
              libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_+0x1a3
              libjvm.so`__1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_+0x27
              libjvm.so`__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_+0x2f
              libjvm.so`__1cJJavaCallsMcall_virtual6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_+0xc1
              libjvm.so`__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_+0x7e
              libjvm.so`__1cMthread_entry6FpnKJavaThread_pnGThread__v_+0xd2
              libjvm.so`__1cKJavaThreadRthread_main_inner6M_v_+0x4c
              libjvm.so`__1cKJavaThreadDrun6M_v_+0x196
              libjvm.so`java_start+0xd3
              libc.so.1`_thr_setup+0x52
              libc.so.1`_lwp_start
             1006

              libjvm.so`jni_CallStaticVoidMethod+0xe0
              libmawt.so`X11SD_Unlock+0x1d4
              libawt.so`Java_sun_java2d_loops_MaskFill_MaskFill+0x20c
              sun/java2d/loops/MaskFill.MaskFill(Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Ljava/awt/Composite;IIII[BII)V*
              sun/java2d/pipe/AlphaColorPipe.renderPathTile(Ljava/lang/Object;[BIIIIII)V*
              sun/java2d/pipe/DuctusShapeRenderer.renderPath(Lsun/java2d/SunGraphics2D;Ljava/awt/Shape;Ljava/awt/BasicStroke;)V*
              sun/java2d/pipe/DuctusShapeRenderer.fill(Lsun/java2d/SunGraphics2D;Ljava/awt/Shape;)V*
              sun/java2d/pipe/PixelToShapeConverter.fillRect(Lsun/java2d/SunGraphics2D;IIII)V*
              sun/java2d/S
              0xfc6a5f35
              0xfc402e9d
              0xfc6dc99d
              0xfc402e9d
              0xfc402e9d
              0xfc758abc
              0xfc76f8fa
              0xfc402c71
              0xfc402c71
              0xfc6947fe
              0xfc782015
              0xfc75cc9c
              0xfc402e9d
              0xfc402e9d
              0xfc402e9d
              0xfc768b95
              0xfc699bb4
              0xfc5cd0d0
              0xfc402c71
              0xfc402e9d
              0xfc402e9d
              0xfc402e9d
              0xfc402e9d
              0xfc400202
              libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_+0x1a3
              libjvm.so`__1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_+0x27
              libjvm.so`__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_+0x2f
              libjvm.so`__1cJJavaCallsMcall_virtual6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_+0xc1
              libjvm.so`__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_+0x7e
              libjvm.so`__1cMthread_entry6FpnKJavaThread_pnGThread__v_+0xd2
              libjvm.so`__1cKJavaThreadRthread_main_inner6M_v_+0x4c
              libjvm.so`__1cKJavaThreadDrun6M_v_+0x196
              libjvm.so`java_start+0xd3
              libc.so.1`_thr_setup+0x52
              libc.so.1`_lwp_start
             1010

그 중에 가장 호출 빈도수가 높은 경우는 위와 같다. (c++filt를 쓰지 않아서 mangling 되어있다)
자바가 화면에 새로 그리기 위해서 호출이 많은 것 같다(필자는 자바 전문가가 아니라서 자세히 설명하진 못하겠다. 하지만, 애플리케이션 그래픽 프로그램이고 이벤트를 받지 않으면 별로 하는 일이 없기 때문에 이렇게 나오는게 아닌가 싶다.)

mangling이 답답하다면 demangle 해서 보자.

자바를 아는 자바 전문가들에게는 진짜 필요한 툴이 아닌가 싶다.
참고로, dtrace는 솔라리스10에서만 사용가능하므로, 솔라리스에서만 이렇게 dtrace를 통한 java trace가 가능하다.

문제를 알았다면, 이제 자바 전문가를 불러서 해결해달라고 하자. ㅡ.ㅡ;

Dtrace 활용하기 : Xorg의 버그 finding

오픈 솔라리스를 쓰다보면 확실히 많은 버그에 시달리게 된다. 그나마 썬 솔라리스는 패치도 제공해주고 하니, 가끔씩 패치하면 버그가 줄어드는 것을 볼 수가 있는데, 오픈 솔라리스는 그것도 못하니 새로 깔아야 한단 얘긴데... 참으로 귀찮은 일이 아닐 수 없다.

한편으론 dtrace를 활용하면서 배워보려고 하는 사람들에겐 아주 좋은 환경이 아닐 수 없다.
여기 저기 떠돌아다니는 버그들 덕분에 dtrace 활용율을 늘릴 수는 있을 테니 말이다.

오늘은 firefox 2.0 영문 브라우저를 쓰고 있는데, 미디어 다음 사이트를 접속만 하면 시스템의 CPU가 버닝아웃될 지경이다.(참고로 내 시스템은 Mobile Athlon 3000+/amd64/2G from Acer )

그래서, dtrace로 Xorg의 어느 부분이 문제인지 찾아보고 싶어졌다. 평소에는 바뻐서 그냥 죽였다 다시 살리곤 했는데, 오늘은 시간이 좀 되는 관계로...
prstat상으로 보면, 나는 브라우저를 사용하고 있는데, CPU 점유는 Xorg가 거의 다하고 있다.
Xorg가 누구 때문에 이렇게 열심히 일하는 지를 보려면 /usr/demo/dtrace/whofor.d를 이용하면
쉽게 추측할 수 있다(물론, prstat만 가지고도 어느 정도 예측할 수 있지만  말이다)

#dtrace -s /usr/demo/dtrace/whofor.d

firefox-bin sleeping on Xorg:

           value  ------------- Distribution ------------- count
            2048 |                                         0
            4096 |                                         5
            8192 |@@@@@@@@@@@@@@@                          6102
           16384 |@@@@@@@@@@@@@                            5053
           32768 |@@@@                                     1703
           65536 |@@@                                      1057
          131072 |@                                        587
          262144 |@                                        358
          524288 |@                                        199
         1048576 |@                                        288
         2097152 |@                                        244
         4194304 |                                         148
         8388608 |                                         43
        16777216 |                                         15
        33554432 |                                         16
        67108864 |                                         2
       134217728 |                                         0


챠트상으로 나온 결과를 보면, firefox가 잦은 횟수로 Xorg에 CPU를 빼앗기고 있다.
firefox가 Xorg에 부담을 주고 있는 것으로 예측할 수 있다.

그럼 Xorg의 뭐가 잘못되서 CPU를 이렇게 쓰는 것인지 궁금해졌다. '도대체 Xorg를
왜 이렇게 만들어가지설라무네...'
일단 CPU를 과점하고 있는 Xorg의 pid로 pstack을 찍어봤다. 흠...
Xorg의 문제인지, shared library의 문제인지가 궁금하기 때문이다.
_XSERVTransSocketRead() 란 함수가 불려지고 있는 것으로 나오는데 이게 어느 라이브러리에 붙어있는 것인지 알 수가 없다.

dtrace를 떴다.
#dtrace -b 2g  -n 'pid$target::_XSERVTransSocketRead:entry{ustack();}' -p `pgrep Xorg`
Xorg의 문제이다.

여기서 _XSERVT* 함수가 불릴때만 문제가 되는 것인지 확정할 수 없다. 일단 Xorg가 문제임으로 Xorg의 Sub function들을 대상으로 호출 회수 상태를 알고 싶어졌다. 다시 dtrace를 했다.

#dtrace  -n 'pid$target:Xorg::entry{@[probefunc]=count()}' -p `pgrep Xorg`
Xalloc, Xfree를 과도하게 하고 있는 것이 보인다.
이 함수는 둘다 Xorg의 메모리 할당과 해제를 위한 함수인데, 뭐 때문에 메모리 할당 해제를
이렇게 심하게 반복할까? 궁금해진다. 다시 dtrace를 돌렸다.

Xalloc이 호출될때, 어떤 stack을 경유해서 Xalloc() 이 호출되는 지 궁금하기 때문이다.
이 부분을 알게 되면, Xorg의 문제의 중요 부분을 알 수 있겠다 싶다.

# dtrace  -n 'pid$target:Xorg:Xalloc:entry{@[ustack()]=count();}' -p `pgrep Xorg`
              Xorg`Xalloc
              libxaa.so`XAAInitPixmapCache+0x72b
              Xorg`localAllocateOffscreenArea+0x63
              Xorg`xf86AllocateOffscreenArea+0x36
              libxaa.so`XAACreatePixmap+0x226
              Xorg`ProcCreatePixmap+0xff
              Xorg`Dispatch+0x2d2
              Xorg`main+0x4de
              Xorg`_start+0x7a
             1932

              Xorg`Xalloc
              libxaa.so`XAAValidateGC+0x305
              Xorg`damageValidateGC+0x40
              Xorg`ValidateGC+0x1a
              Xorg`ProcPolyFillRectangle+0x101
              Xorg`Dispatch+0x2d2
              Xorg`main+0x4de
              Xorg`_start+0x7a
             3281

              Xorg`Xalloc
              Xorg`miRegionCreate+0xf
              Xorg`miComputeCompositeClip+0x98
              libfb.so`fbValidateGC+0x63
              Xorg`miBSCheapValidateGC+0x89
              libxaa.so`XAAValidateGC+0x63
              Xorg`damageValidateGC+0x40
              Xorg`ValidateGC+0x1a
              Xorg`ProcPolyFillRectangle+0x101
              Xorg`Dispatch+0x2d2
              Xorg`main+0x4de
              Xorg`_start+0x7a
             3382

              Xorg`Xalloc
              Xorg`AddResource+0xda
              Xorg`ProcCreateGC+0xbd
              Xorg`Dispatch+0x2d2
              Xorg`main+0x4de
              Xorg`_start+0x7a
             4191

              Xorg`Xalloc
              Xorg`CreateGC+0x1a
              Xorg`ProcCreateGC+0xa5
              Xorg`Dispatch+0x2d2
              Xorg`main+0x4de
              Xorg`_start+0x7a
             4191

              Xorg`Xalloc
              Xorg`miRectsToRegion+0x13
              Xorg`miChangeClip+0x35
              Xorg`miBSCheapChangeClip+0x27
              libxaa.so`XAAChangeClip+0x43
              Xorg`damageChangeClip+0x44
              Xorg`SetClipRects+0xe1
              Xorg`ProcSetClipRectangles+0x87
              Xorg`Dispatch+0x2d2
              Xorg`main+0x4de
              Xorg`_start+0x7a
             4949

              Xorg`Xalloc
              Xorg`SetClipRects+0x3f
              Xorg`ProcSetClipRectangles+0x87
              Xorg`Dispatch+0x2d2
              Xorg`main+0x4de
              Xorg`_start+0x7a
             4949

              Xorg`Xalloc
              libxaa.so`XAADoGlyphs+0x5b7
              libxaa.so`XAAGlyphs+0x95
              Xorg`damageGlyphs+0x337
              Xorg`CompositeGlyphs+0x13e
              Xorg`ProcRenderCompositeGlyphs+0x347
              Xorg`ProcRenderDispatch+0x26
              Xorg`Dispatch+0x2d2
              Xorg`main+0x4de
              Xorg`_start+0x7a
             6327



다시 dtrace를 돌렸다.
흠... 끝의 다섯개의 스택에 관심이 갔다. 특히, 마지막 스택을 유념하게 보게된다.
그 앞의 스택의 호술 횟수(4949)보다 50%이상 많다(6327)

ProcRenderCompositeGlyphs() Glyphs 이라.. 어라, 이건 폰트 문제인데... Glyphs를 damage()
한후 libxaa에 있는 XAAGlyphs를 다시 호출하느라 메모리를 할당하고 있네...
왜 이러지 미쳤나? xaa 라이브러리는 True type 폰트를 위한 anti-aliasing 라이브러리이다.
firefox가 열려고 시도한 웹페이지에서 지정한 true-type 폰트의 anti-aliasing 때문에
생긴 것일까?

어느 정도 원인은 파악했다. 역시 dtrace다 !!!

그런데, 이걸 어떻게 해결해야 하나...가 고민이다. Xorg를 업그레이드 해야 하나? 솔라리스를 다시 깔아야 하나? 아니면, 옵션 변경으로 할 수 있을까?

여기서부터는 dtrace문제 밖인데... 웬지 dtrace가 해결까지 해줬으면 좋겠다는 꿈을 꿔본다.

"도움이 되셨으면 구글 애드센스 살짝 눌러주는 센스를 기대하겠습니다"



2007/04/16

윈도우즈 비스타에서 NTFS 크기 변경

비스트가 설치되어 있는 시스템에서 솔라리스를 설치하려면 일단 디스크 파티
션을
재조정할 필요가 있습니다. 포맷되어 있지 않은 디스크 영역이 있다면 모를까
일반적으로는 시스템이 공급될때 부터 하나의 디스크 혹은 볼륨으로 구성되어서
오므로, 솔라리스나 리눅스를 설치하고자 한다면, 파티션을 새로이 조정해야
하는데

윈도우즈 비스타이전에는 파티션 매직이나 Gparted와 같은 제3의 유틸리티 혹은
오픈 소스 유틸리티를 이용하여 다시 파티셔닝을 했었습니다. 그러나, 비스타
에서는
파티션을 재조정할 수 있는 툴을 자체적으로 제공합니다.

이 툴을 이용하여 100% 사용하고 있는 NTFS 파티션을 10GB 줄이게 되면, 10GB
규모의
빈공간의 디스크가 나오게 되는데, 이공간에 솔라리스를 설치하면 됩니다.

NTFS 확장이나 축소를 위한 툴은 Control Panel(제어판) -> System and
Management(시스템 관리) -> Administrative Tools(관리툴) -> Computer
Management(컴퓨터 관리) -> Disk Management(디스크 관리)

에서 접근 가능합니다. 사용방법 별로 어렵지 않으며, 마우스 우측 버튼을 통
하여 쉽게 해결할 수 있습니다. 명령어로도 가능합니다.

원래 소스는 아래 페이지를 참고하세요.
http://www.bleepingcomputer.com/tutorials/tutorial133.html

* 도움이 되셨다면 위 Google Adsense 살짝 클릭해주는 센스 기대합니다. *