Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Serviceability Toolsの裏側

3,821 views

Published on

Published in: Technology

Serviceability Toolsの裏側

  1. 1. ~どうやって情報をとってるの?~2013/05/11末永 恭正 @YaSuenag #ccc_r54
  2. 2. 自己紹介 末永 恭正 @YaSuenag 某通信キャリアでJavaの障害解析してますした hs_errorログやコアの解析 などなど ネイティブ寄りなJavaプログラマ 基本サンデープログラマー PascalとかCとかJavaとか…すえなが やすまさ
  3. 3. Serviceability Tools ? Serviceability in the J2SE Repository http://openjdk.java.net/groups/serviceability/svcjdk.htmljconsolejdbjhatjinfojmapjpsjcontroljsadebugdjstackjstatjstatd
  4. 4. Serviceability Technologieshttp://openjdk.java.net/groups/serviceability/
  5. 5. ツールとJVMがカンタンに連携
  6. 6. Dynamic Attach ツールからの要求に応じてHotSpotが情報収集 各ツールとHotSpotの連係プレー ツール側○ sun.tools.attach.HotSpotVirtualMachineの提供するメソッドを実行○ 専用のコマンドと引数をターゲットVMへ送信 HotSpot側○ Attach Listenerが指定された処理を実行し、結果をツールへ返却"Attach Listener" daemon prio=10 tid=0x00007f92dc001000 nid=0x261e runnable [0x0000000000000000]java.lang.Thread.State: RUNNABLE
  7. 7. 各ツールとHotSpotの連携Javaプロセス(HotSpot VM)各種ツール①SIGQUIT送信※Linuxの場合②AttachListener起動③コマンド送信④処理実行④´処理結果Unixドメインソケット
  8. 8. AttachListenerのコマンド フォーマット<ver>0<cmd>0<arg>0<arg>0<arg>0 AttachListenerに与える命令 ヌル文字(0)がデリミタ 項目 ver○ プロトコルバージョン(’1’固定) cmd○ コマンド arg○ コマンド引数(最大3つまで)
  9. 9. AttachListenerのコマンドコマンド 機能 利用ツールagentProperties エージェント用システムプロパティの取得• jdb• jconsole• JMXエージェントdatadump SIGQUIT送出(と等価) なしdumpheap ヒープダンプ生成 jmapload JVMTIエージェントのロード jconsoleproperties システムプロパティの取得 • jconsole• jinfothreaddump スレッドダンプの取得 jstackinspectheap クラスヒストグラムの取得 jmapsetflag 非推奨オプション(-XX)の設定 jinfoprintflag 非推奨オプション(-XX)の取得 jinfojcmd jcmdの実行 jcmdenabledprobes DTraceプローブを有効にする※Solarisのみなし
  10. 10. 超簡単!AttachListener利用 Javaプログラム中の特定のポイント通過時のクラスヒストグラムを取ってみる Javaプログラムから、自分自身のVMにアタッチ HotSpotVirtualMachine#heapHisto()を呼び出す○ AttachListener的にはinspectheapコマンドを実行する LinuxのJava7以降で動くものを作ります
  11. 11. Javaプログラム ※抜粋import java.io.*;import java.nio.file.*;import com.sun.tools.attach.*;import sun.tools.attach.*;:Path selfProc = FileSystems.getDefault().getPath("/proc/self");String pidString = Files.readSymbolicLink(selfProc).toString();HotSpotVirtualMachine selfVM =HotSpotVirtualMachine)VirtualMachine.attach(pidString);try(InputStream in = selfVM.heapHisto("-all")){byte[] buf = new byte[1024];int n;while((n = in.read(buf)) > 0){System.out.print(new String(buf, 0, n, "UTF-8"));}}finally{selfVM.detach();}:PID取得アタッチAttachListenerへコマンド送信結果読み込み&出力デタッチ
  12. 12. ビルド&実行$ javac -cp $JAVA_HOME/lib/tools.jar AttachListenerTest.java$ java -cp $JAVA_HOME/lib/tools.jar:. AttachListenerTestnum #instances #bytes class name----------------------------------------------1: 13228 1462888 [C2: 7477 1136592 <constMethodKlass>3: 7477 1022984 <methodKlass>4: 562 701568 <constantPoolKlass>5: 2093 536728 [B6: 562 396376 <instanceKlassKlass>7: 502 395520 <constantPoolCacheKlass>8: 579 265768 [I9: 6120 146880 java.lang.String10: 631 76696 java.lang.Class11: 1003 65664 [S12: 882 55808 [Ljava.lang.Object;13: 2278 54672 java.lang.StringBuilder14: 900 52952 [[I15: 52 29536 <objArrayKlassKlass>:ソケットバッファ溢れに注意!
  13. 13. JVMにインパクトを与えずにディープな情報を取得!
  14. 14. jvmstat Performance Counters 通称”PerfCounter” HotSpotの統計情報を集めたファイル /tmp/hsperfdata_<ユーザー名>/<PID> 各種データは、このファイルにマップされた仮想メモリ空間に直接保存 GC回数/時間 ライブスレッド数 etc… 各種ツールはこのファイルを直接参照してJVMの動作状況を収集する
  15. 15. 具体的に見てみる jcmdのPerfCounter.print$ jcmd 2065 PerfCounter.print2065:java.ci.totalTime=0java.cls.loadedClasses=379java.cls.sharedLoadedClasses=0java.cls.sharedUnloadedClasses=0java.cls.unloadedClasses=0:sun.rt.safepointSyncTime=22sun.rt.safepointTime=80sun.rt.safepoints=1sun.rt.threadInterruptSignaled=0sun.rt.vmInitDoneTime=1365171171705sun.threads.vmOperationTime=29sun.urlClassLoader.readClassBytesTime=165372sun.zip.zipFile.openTime=1010449sun.zip.zipFiles=2Javaレイヤから出してるPerfCounter6878481: Add performance counters in the JDK
  16. 16. トラップ!その1 tmpwatch /tmpや/var/tmpを定期的にお掃除するツール○ Red Hat系ディストロではおなじみ /tmp/hsperfdata_<ユーザー名>も消しやがる!○ psではプロセスが見えるのにJDK付属ツールでアタッチできない場合に疑うべきポイント○ RHBZ#527425 - /tmp/hsperfdata_${user}/${PID} isdeleted by tmpwatch やっぱり同じことで困ってる人がいました Fedoraならtmpwatch-2.9.16-1.fc13からOKだそうです
  17. 17. トラップ!その2 java.io.tmpdir 6u23、6u24のみ該当○ 6938627: Make temporary directory use propertyjava.io.tmpdir when specified○ 7009828: Fix for 6938627 breaks visualvmmonitoring when -Djava.io.tmpdir is defined 6938627が入ったことで、各種ツールが動かなくなってしまう人が続出○ 実はDynamic Attachも影響を受けますHotSpot混迷の時代
  18. 18. 超簡単!カスタム情報収集ツール PerfCounterから自分が欲しい情報を好きなフォーマットで出力します 出力する情報 ライブスレッド数 モニタ競合回数 VM総停止時間 アプリケーション固有の統計情報○ プログラムからPerfCounterにエントリを作ります
  19. 19. Javaプログラム ※抜粋 PerfCounter生成メソッド sun.misc.PerfCounter#newPerfCounterを呼び出す Package privateなのでリフレクションで呼び出しprivate PerfCounter newPerfCounter(String name) throws Exception{Method method = PerfCounter.class.getDeclaredMethod("newPerfCounter", String.class);method.setAccessible(true);PerfCounter result = (PerfCounter)method.invoke(null, name);return result;}
  20. 20. Javaプログラム ※抜粋 呼び出し エントリ名はとりあえず以下のようにしてみる○ perfCounter.test.<スレッド名>.invocationsString entryName = “perfCounter.test.” +Thread.currentThread().getName() + ".invocations";try{this.perfCounter = this.newPerfCounter(entryName);
  21. 21. jstat_options jstatが使用する定義ファイル PerfCounterから収集する項目と出力方法を定義したもの 割合みたいな単純計算まで定義できます $JAVA_HOME/lib/tools.jarに含まれてます $HOME/.jvmstat/jstat_optionsでオーバーライド可能
  22. 22. カスタム版jstat_optionsoption custom{column {header "^Threads^"data java.threads.livescale rawalign rightwidth 4format "0"}column {header "^MonitorContention^"data sun.rt._sync_Parksscale rawalign rightwidth 4format "0"}column {header "^STW^"data (sun.rt.safepointTime/sun.os.hrt.frequency)scale rawalign rightwidth 6format "0.000"}column {header "^ThreadA^"data perfCounter.test.ThreadA.invocationsscale rawalign rightwidth 3format "0"}column {header "^ThreadB^"data perfCounter.test.ThreadB.invocationsscale rawalign rightwidth 3format "0"}column {header "^ThreadC^"data perfCounter.test.ThreadC.invocationsscale rawalign rightwidth 3format "0"}}
  23. 23. 実行結果$ jstat -custom 1806Threads MonitorContention STW ThreadA ThreadB ThreadC7 21 0.117 7 6 5インターバル設定しても変化がみられません8011934 : sun.misc.PerfCounter callsPerf.createLong with incorrect parameters
  24. 24. ネイティブレイヤから直接情報を引っこ抜く最強ツール!
  25. 25. Serviceability Agent 通称”SA” 機能 強制的なプロセスアタッチ○ Linuxではptrace(2)を使用○ jinfoやjmap等の”-F”オプションで発動○ jstackでは”-m”オプションでも発動 コア解析○ LinuxではコアイメージからJavaレイヤの情報(スレッドダンプやヒープダンプ)などを取得可能○ ELFバイナリをdebuginfoまで含め、自力でパースしてしまう!
  26. 26. SAの情報ソース① シンボルテーブル 各バイナリを解析して、仮想メモリ空間のどこに、何が入っているのかを特定② VMStructs JVM中で使用する情報を保持する仮想メモリアドレスや各種オフセット位置をまとめた構造体配列 JavaオブジェクトのC++表現(oop)のパースなど、様々な用途で使用
  27. 27. VMStructsの中身 マクロで定義されてます hotspot/src/share/vm/runtime/vmStructs.cpp:nonstatic_field(CollectedHeap, _reserved, MemRegion) nonstatic_field(SharedHeap, _perm_gen, PermGen*) nonstatic_field(CollectedHeap, _barrier_set, BarrierSet*) nonstatic_field(CollectedHeap, _defer_initial_card_mark, bool) nonstatic_field(CollectedHeap, _is_gc_active, bool) nonstatic_field(CompactibleSpace, _compaction_top, HeapWord*) nonstatic_field(CompactibleSpace, _first_dead, HeapWord*) nonstatic_field(CompactibleSpace, _end_of_live, HeapWord*) :
  28. 28. トラップ!その1 Linux x86_64環境で動かないことがあります 7003789: PTRACE_GETREGS problems with SAon Linux.○ JDK6u25、JDK7以降ならFix済み$ /usr/local/jdk1.6.0_14/bin/jstack -F 8675Attaching to process ID 8675, please wait...Debugger attached successfully.Server compiler detected.JVM version is 14.0-b16Deadlock Detection:No deadlocks found.Thread 8693: (state = BLOCKED)Error occurred during stack walking:sun.jvm.hotspot.debugger.DebuggerException:sun.jvm.hotspot.debugger.DebuggerException: get_thread_regs failed for a lwp
  29. 29. トラップ!その2 Linux環境でコアイメージを読み込めないことがあります 7133122: SA throwssun.jvm.hotspot.debugger.UnmappedAddressException when it should not○ 全JDKに影響○ 2012/01にパッチを出したものの、なかなか入れてもらえない…$ /usr/local/jdk1.6.0/bin/jstack /usr/local/jdk1.6.0/bin/java core.1249Attaching to core core.1249 from executable /usr/local/jdk1.6.0/bin/java, please wait...Error attaching to core file: Cant attach to the core file
  30. 30. じゃあ、直してみる OpenJDK パッチ当て3分クッキング1. OpenJDKのソースを落としてきます2. パッチを当てます3. ビルドします4. libsaproc.soを、使いたいJDKにコピーしますこれだけ!
  31. 31. 1.OpenJDKのソース入手 mercurial (hg) を使います$ hg clone http://hg.openjdk.java.net/jdk8/jdk8複製先ディレクトリ: jdk8全リビジョンを取得中リビジョンを追加中マニフェストを追加中ファイルの変更を追加中666 個のリビジョン(871 の変更を 114 ファイルに適用)を追加ブランチ default へ更新中ファイルの更新数 97、 マージ数 0、 削除数 0、 衝突未解消数 0$ cd jdk8/$ sh get_source.sh# Repositories: corba jaxp jaxws langtools jdk hotspot nashorn:リポジトリのcloneforest構成リポジトリの取得
  32. 32. 2.パッチ当て パッチを入手します CR#7133122のCommentsからコピペ serviceability-devの投稿メールから取得○ http://mail.openjdk.java.net/pipermail/serviceability-dev/2012-January/005174.html JDK8のhotspotでpatchします$ patch -p1 < address_mapping.patchpatching file agent/src/os/linux/ps_core.cpatch unexpectedly ends in middle of lineHunk #2 succeeded at 711 with fuzz 1 (offset -1 lines).
  33. 33. 3.ビルド 最近のOpenJDKはconfigure→make 依存関係は適宜解決してください…$ chmod a+x configure$ ./configure MILESTONE=TypeSRunning generated-configure.sh:* Memory limit: 2000 MB* ccache status: installed and in use$ make:configureに実行権限がついていませんMILESTONEで好きな文字列をバージョンに入れられます
  34. 34. 4.libsaprocの移植 SAのキモです 関数インターフェースがそんなに変わらない(と思う)のでJDK6/7へ持って行っても大丈夫(なはず) libsaproc.soをコンパイルしたものに入れ替え $JAVA_HOME/jre/lib/<CPU>にあります <CPU>には”amd64”や”i386”などが入ります
  35. 35. コアを食べさせてみる JDK6 GAで試してみました$ /usr/local/jdk1.6.0/bin/jstack /usr/local/jdk1.6.0/bin/java core.1249Attaching to core core.1249 from executable /usr/local/jdk1.6.0/bin/java, please wait...Debugger attached successfully.Server compiler detected.JVM version is 1.6.0-b105Deadlock Detection:No deadlocks found.Thread 1254: (state = BLOCKED)Thread 1253: (state = BLOCKED)- java.lang.Object.wait(long) @bci=0 (Interpreted frame)- java.lang.ref.ReferenceQueue.remove(long) @bci=44, line=116 (Interpreted frame)- java.lang.ref.ReferenceQueue.remove() @bci=2, line=132 (Interpreted frame)- java.lang.ref.Finalizer$FinalizerThread.run() @bci=3, line=159 (Interpreted frame):動いた!
  36. 36. なにか忘れてない?http://openjdk.java.net/groups/serviceability/jcmd
  37. 37. jcmd Dynamic AttachとPerfCounterの両面を併せ持つツール コマンド実装はJVM側だけでOK! 機能追加/改造がラクチン!
  38. 38. じゃあ、改造してみる OpenJDK jcmd改造3分クッキング とりあえず、コンパイル済みコードの一覧を出してみます○ CodeCacheの内容をダンプしてみます○ CodeCache.printというコマンドにしてみます1. OpenJDKのソースを落としてきます2. jcmdの実装部を追加します3. ビルドしますこれだけ!
  39. 39. 1.OpenJDKのソース入手 省略
  40. 40. 2.改造 下ごしらえ CodeCache::print_internalがstdout以外のストリームに出力できるようにします○ hotspot/src/share/vm/code/codeCache.cpp○ hotspot/src/share/vm/code/codeCache.hppstatic void print_internals(outputStream *tty = tty);出力先ストリーム追加void CodeCache::print_internals(outputStream *tty) {
  41. 41. 2.改造 DiagnosticCommand用クラスの定義 hotspot/src/share/vm/services/diagnosticCommand.hpp#ifndef PRODUCTclass PrintCodeCacheDCmd : public DCmd {public:PrintCodeCacheDCmd(outputStream* output, bool heap): DCmd(output,heap) { }static const char* name() { return "CodeCache.print"; }static const char* description() {return "Print compiled methods in CodeCache.";}static const char* impact() { return “???"; }static int num_arguments() { return 0; }virtual void execute(TRAPS);};#endifどうしよ…?
  42. 42. “Impact”とは? 各コマンドのhelpで表示される部分 JEP 137: Diagnostic-Command Framework 定義がものすごく曖昧○ JVMへの影響度合いをLow / Medium / Highで分類○ 膨大なスレッドダンプやヒープダンプは深刻、バージョン番号等の取得は大した影響なしと定義 http://openjdk.java.net/jeps/137$ jcmd 1563 help VM.flags:Impact: Low::
  43. 43. 各コマンドとインパクトコマンド 内容 インパクトhelp コマンドのヘルプ表示 LowVM.version JVMバージョンの表示 LowVM.command_line コマンドライン引数の表示 LowVM.system_properties システムプロパティの表示 LowVM.flags 非推奨(-XX)オプションの表示 LowVM.uptime JVM起動時間の表示 LowGC.run_finalization ファイナライザの実行 MediumGC.run GCの実行 MediumGC.heap_dump ヒープダンプの生成 HighGC.class_histogram クラスヒストグラムの表示 HighGC.class_stats クラス統計情報の表示 HighThread.print スレッドダンプの表示 MediumManagementAgent.start JMXエージェントの起動 No impactManagementAgent.start_local JMXエージェントの起動(ローカル限定) No impactManagementAgent.stop JMXエージェントの停止 No impactsafepoint “Depends onJava content.”
  44. 44. safepoint いわゆるSTW アプリケーションスレッド全停止 正式な定義はコチラ○ http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html#safepoint jcmdでは、STWを伴うコマンドは最低でもMedium以上に定義されている模様 Dynamic Attachでは以下がsafepointで動作 jstack jmap
  45. 45. 結論 実行にSTWを伴う JVMの状態によっては処理が高コストになりうる上記以外であればLowでOK…と、勝手に解釈
  46. 46. 2.改造 CodeCacheの全走査になるので、とりあえずMediumに設定#ifndef PRODUCTclass PrintCodeCacheDCmd : public DCmd {public:PrintCodeCacheDCmd(outputStream* output, bool heap): DCmd(output,heap) { }static const char* name() { return "CodeCache.print"; }static const char* description() {return "Print compiled methods in CodeCache.";}static const char* impact() { return “Medium"; }static int num_arguments() { return 0; }virtual void execute(TRAPS);};#endif
  47. 47. 2.改造 PrintCodeCacheDCmdの登録 void DCmdRegistrant::register_dcmds()に初期化コードを追加 hotspot/src/share/vm/services/diagnosticCommand.cpp#ifndef PRODUCTDCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PrintCodeCacheDCmd>(true, false));#endif
  48. 48. 2.改造 コマンド実行関数の実装 hotspot/src/share/vm/services/diagnosticCommand.cpp#ifndef PRODUCTvoid PrintCodeCacheDCmd::execute(TRAPS) {MutexLockerEx mu(CodeCache_lock,Mutex::_no_safepoint_check_flag);CodeCache::print_internals(output());}#endifロック忘れずに!
  49. 49. 3.ビルド 省略ですが…debugモードでビルド!./configure --with-debug-level=fastdebug
  50. 50. 実行結果 Tomcat7にjcmd CATALINA_OPTSに-XX:+Verboseを付加$ ./build/linux-x86_64-normal-server-fastdebug/jdk/bin/jcmd31376 org.apache.catalina.startup.Bootstrap start31390 sun.tools.jcmd.JCmd$ ./build/linux-x86_64-normal-server-fastdebug/jdk/bin/jcmd 31376 CodeCache.print31376:java.util.zip.InflaterInputStream.ensureOpen()V alivejava.lang.Integer.rotateLeft(II)I alivejava.util.zip.ZStreamRef.address()J alivejava.lang.String.charAt(I)C alivejava.lang.String.indexOf(II)I alivesun.misc.Hashing.murmur3_32(I[CII)I alivejava.lang.Object.<init>()V alivesun.nio.cs.UTF_8$Encoder.encode([CII[B)I alivejava.lang.String.equals(Ljava/lang/Object;)Z alivejava.lang.System.arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V alivejava.lang.String.lastIndexOf(II)I alive:
  51. 51. 何か、新しいツールはないだろうか?
  52. 52. JVMからOSまで低オーバーヘッドで情報収集!
  53. 53. HeapStats 障害解析に必要な情報を根こそぎ収集! GC動作状況、直後のクラスヒストグラム OSリソース(CPU/メモリ) SPECjvm2008でのオーバーヘッドは5%以下 OOMEやデッドロックをリアルタイム検知 SNMPトラップも送出します 解析はGUIツールでラクチン! OS・Javaの情報を横並びで比較可能Serviceability Toolsのエッセンスも実装!
  54. 54. こんなにいっぱい見れます!
  55. 55. HeapStats Community HeapStats @ IcedTea http://icedtea.classpath.org/wiki/HeapStats ML heapstats@icedtea.classpath.org http://icedtea.classpath.org/mailman/listinfo/heapstats

×