狠狠撸

狠狠撸Share a Scribd company logo
末永 恭正 @YaSuenag
https://github.com/YaSuenag/jdt-2017-examples
OracleとJavaは、Oracle Corporation及びその子会社、関連会社の米国及びその他の国における登録商標です。
文中の社名、商品名等は各社の商標または登録商標である場合があります。
?
?
?
?
?
?
?
?
?
?
?
?
http://icedtea.classpath.org/wiki/HeapStats/jp
?
?
https://bugs.openjdk.java.net/
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
? http://openjdk.java.net/jeps/243
?
?
?
?
?
JITコンパイラ
クラスライブラリ
アプリケーション
Graal
HotSpot
JVMCI
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
1.
2.
?
3.
4.
?
5.
1. ランタイム取得 2. バックエンド取得
3. プロバイダ取得
4. メタデータ取得
闯顿碍内部モジュールです
?
?
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
?
バイトコード
インタープリタ
JIT
Tier 0
Tier 1~4
Deopt
プロファイリング
プロファイリング
JITコンパイルに
かかったか?
コンパイルレベル
(Tier)
メソッドメタデータ取得
(HotSpotResolvedJavaMethod)
hasCompiledCode = true
Compile level = 3
?
プロファイリング情報の
取得
十分
プロファイルできた?
実行回数
例外発生の有無
分岐の割合
0.0(分岐しない)~1.0(全部分岐)
HotSpotProfilingInfo<exceptionSeen@0: FALSE; exceptionSeen@1: FALSE; exceptionSeen@2:
FALSE; exceptionSeen@3: FALSE; exceptionSeen@4: FALSE; exceptionSeen@5: FALSE;
executionCount@6: 501760; branchProbability@6: 0.000002; exceptionSeen@6: FALSE;
exceptionSeen@7: FALSE; exceptionSeen@8: FALSE; exceptionSeen@9: FALSE; exceptionSeen@10:
FALSE; exceptionSeen@11: FALSE; exceptionSeen@12: FALSE; exceptionSeen@13: FALSE;
exceptionSeen@14: FALSE; executionCount@15: 501759; branchProbability@15: 1.000000;
exceptionSeen@15: FALSE; exceptionSeen@16: FALSE; exceptionSeen@17: FALSE;
exceptionSeen@18: FALSE; exceptionSeen@19: FALSE>
Compile level = 3
isMature = true
BCI 6: execution count = 501760, branch = 1.992984693877551E-6
BCI 15: execution count = 501759, branch = 1.0
public static int runLoop(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: iconst_0
1: istore_1
2: iconst_1
3: istore_2
4: iload_2
5: iload_0
6: if_icmpgt 18
9: iinc 1, 1
12: iinc 2, 1
15: goto 4
18: iload_1
19: ireturn
ほとんど分岐しない
(0.000002)
上にジャンプ
(1.0)
HotSpotProfilingInfo<exceptionSeen@0: FALSE; exceptionSeen@1: FALSE; exceptionSeen@2:
FALSE; exceptionSeen@3: FALSE; exceptionSeen@4: FALSE; exceptionSeen@5: FALSE;
executionCount@6: 501760; branchProbability@6: 0.000002; exceptionSeen@6: FALSE;
exceptionSeen@7: FALSE; exceptionSeen@8: FALSE; exceptionSeen@9: FALSE; exceptionSeen@10:
FALSE; exceptionSeen@11: FALSE; exceptionSeen@12: FALSE; exceptionSeen@13: FALSE;
exceptionSeen@14: FALSE; executionCount@15: 501759; branchProbability@15: 1.000000;
exceptionSeen@15: FALSE; exceptionSeen@16: FALSE; exceptionSeen@17: FALSE;
exceptionSeen@18: FALSE; exceptionSeen@19: FALSE>
Compile level = 3
isMature = true
BCI 6: execution count = 501760, branch = 1.992984693877551E-6
BCI 15: execution count = 501759, branch = 1.0
100万回動かしてるはずなんだけど…
MDO
?
?
?
?
実際の呼び出し回数と一致しない!
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
?
?
?
?
?
?
262 167 % 3 TestBase::runLoop @ 4 (20 bytes)
263 168 3 TestBase::runLoop (20 bytes)
263 169 % 4 TestBase::runLoop @ 4 (20 bytes)
264 167 % 3 TestBase::runLoop @ 4 (20 bytes) made not entrant
265 169 % 4 TestBase::runLoop @ 4 (20 bytes) made not entrant
:
runLoop() finished.
1136 168 3 TestBase::runLoop (20 bytes) made not entrant
reprofile.
3466 219 % 4 TestBase::runLoop @ 4 (20 bytes)
3468 220 4 TestBase::runLoop (20 bytes)
3468 219 % 4 TestBase::runLoop @ 4 (20 bytes) made not entrant
runLoop() finished.
リプロファイルで
破棄された
最初の生成
リプロファイル後の
生成コード
?
?
?
? 暖気後
? 妥当な
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
http://cr.openjdk.java.net/~vlivanov/talks/2016_JVMLS_MachineCodeSnippets.pdf
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
Machine Code Snippetsっぽいことができる!
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
?
?
?
?
?
?
?
?
?
? O S R
?
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
?
?
?
s1 s2 s3 s4 s5 s6 s7 s8src
dest d1 d2 d3 d4 d5 d6 d7 d8
+ + + + + + + +
s1+d1 s2+d2 s3+d3 s4+d4 s5+d5 s6+d6 s7+d7 s8+d8
=
=
=
=
=
=
=
=
?
?
?
https://wiki.openjdk.java.net/display/HotSpot/CallingSequences
呼び出し規約の
種類
使えるレジスタたちの取得
呼び出し規約と
型を指定
引数の順番を指定
ハンドアセンブル!
1.
2.
3.
?
vmovdqu <配列>, %ymm1
vpaddd <配列>, %ymm1, %ymm0
vmovdqu %ymm0, <配列>
※GAS形式
?
? https://software.intel.com/en-us/articles/intel-sdm
?
?
?
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
?
?
?
?
Javaの世界の配列を
どうアセンブラの世界で扱うか?
ヘッダ 長さ 中身
引数で渡ってくる配列オブジェクトの先頭アドレス
Unsafe.ARRAY_INT_BASE_OFFSET
?
? O O P
?
?
インストール
コード確定
プロローグ
エピローグ
配列の中身への
オフセット
実行
PAddInt#padd()
に設定
9 9 9 9 9 9 9 9
$ jcmd 11217 Compiler.codelist | grep padd
582 4 PAddTest$PAddInt.padd([I[I)V [0x00007f9f8970a090, 0x00007f9f8970a220 - 0x00007f9f8970a250]
コンソール出力
コードキャッシュ
(gdb) disas 0x00007f9f8970a220, 0x00007f9f8970a250
Dump of assembler code from 0x7f9f8970a220 to 0x7f9f8970a250:
0x00007f9f8970a220: nopl 0x0(%rax,%rax,1)
0x00007f9f8970a225: push %rbp
0x00007f9f8970a226: mov %rsp,%rbp
0x00007f9f8970a229: sub $0x10,%rsp
0x00007f9f8970a230: vmovdqu 0x10(%rdx),%ymm1
0x00007f9f8970a235: vpaddd 0x10(%rsi),%ymm1,%ymm0
0x00007f9f8970a23a: vmovdqu %ymm0,0x10(%rsi)
0x00007f9f8970a23f: mov %rbp,%rsp
0x00007f9f8970a242: pop %rbp
0x00007f9f8970a243: retq
src側配列をAVXレジスタにロード
足し算(SIMD)
計算結果をdest側配列にストア
好きなアセンブラを動かせる
=何でもできるはず!
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
?
?
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
?
?
?
?
?
?
?
?
? https://linuxjm.osdn.jp/html/LDP_man-pages/man2/syscall.2.html
mov $39, %rax # getpid = 39
syscall
※GAS形式
Little Endian
戻り値を
Javaの世界に戻す
JIT生成コードオブジェクト
開始アドレス
システムコール番号
(39)
Installed address: 0x7fda896fba10
1656
$ jcmd 1656 Compiler.codelist | grep 7fda896fba10
569 4 SyscallTest$GetPID.getPid()I [0x00007fda896fba10, 0x00007fda896fbba0 - 0x00007fda896fbbc8]
コンソール出力
コードキャッシュ
(gdb) disas 0x00007fda896fbba0, 0x00007fda896fbbc8
Dump of assembler code from 0x7fda896fbba0 to 0x7fda896fbbc8:
0x00007fda896fbba0: nopl 0x0(%rax,%rax,1)
0x00007fda896fbba5: push %rbp
0x00007fda896fbba6: mov %rsp,%rbp
0x00007fda896fbba9: sub $0x10,%rsp
0x00007fda896fbbb0: mov $0x27,%rax
0x00007fda896fbbb7: syscall
0x00007fda896fbbb9: mov %rbp,%rsp
0x00007fda896fbbbc: pop %rbp
0x00007fda896fbbbd: retq
システムコール番号の設定
システムコール実行
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
?
?
? 関数アドレスの特定
?
? CALL命令の発行
?
?
?
1. どうやってdlsym(3)を呼ぶ?
2.
3.
4.
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
? https://github.com/graalvm
?
?
?
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
?
1.
2. どうやってchar *を生成する?
3.
4.
?
?
?
?
String#getBytes()
→足せばいい
→Unsafe.ARRAY_BYTE_BASE_OFFSET
String→char *
char *→String
?
?
1.
2.
3. どうやって引数を渡せばいい?
4.
?
? http://refspecs.linuxfoundation.org/
?
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
?
1.
2.
3.
4. どうやって戻り値を設定すればいい?
Javaクラスの型から
戻り値に使うレジスタを取得
第1引数
(ライブラリハンドル)
第2引数
(シンボル名へのポインタ)CALL命令発行
戻り値
(ポインタ:long)
RTLD_DEFAULTで探索
(/usr/include/dlfcn.h)
関数アドレスを
CALL命令にかけるコードを
インストール
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
1000
$ id
uid=1000(yasuenag) gid=1000(yasuenag) groups=1000(yasuenag)
context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
コンソール出力
実行ユーザーは…
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
?
? ライブラリのロード
?
?
?
?
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ
?
ヘッダ 長さ 中身
引数で渡ってくる配列オブジェクトの先頭アドレス
ココ
第2引数
(エラー文字列へのポインタ)
第3引数
(エラー文字列の長さ)
ライブラリロード失敗時は
エラーメッセージを表示
ネイティブ関数
呼び出し
関数アドレスを
CALL命令にかけるコードを
インストール
from native
./native/libnativ.so: cannot open shared object file: No such file or directory
もし存在しないライブラリをロードさせたら…
?
?
?
?
?
?
?
?
?
使い方次第で无限の可能性!!
笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ

More Related Content

笔补苍补尘补を先取り!? 闯痴惭颁滨で闯滨罢と游ぶ