JVM 外挂技术

https://mp.weixin.qq.com/s/cwU2rLOuwock048rKBz3ew

入门科普,围绕JVM的各种外挂技术
原创: 江南白衣本衣 春天的旁边 2018-08-01
对围绕JVM的各种工具的外挂技术,运用大整理术,让大家从茫然,到轻摇纸扇,知道分子。

归拢一下,就是C 和 Java两种Agent,SA 和 VirtualMachine 两种 Attach,JMX和PerfData两种Data,两两之间很是混淆,网上好像少了篇简明扼要的科普,所以自己操刀补了一篇,作为上周的直播《VJTools如何利用佛性技术完全JVM》的续集。

最近公众号的阅读量很扑,这篇预计更扑,有缘看进来的同学,帮转。

  1. 两种Agent
    1.1 Native Agent
    以 C/C++代码编写的Agent,用强大的JVMTI(JVM Tool Interface)接口与JVM进行通讯,订阅感兴趣的JVM事件(比如方法出入、线程始末等等),当这些事件发生时,会回调Agent的代码。 JVMTI 同时提供了众多的功能函数,查询和控制 Java 应用的运行状态,包括内存控制和对象获取,线程与锁等等,简直无所不能。

使用者,包括各种Profile工具,如Yourkit,JProfiler,aysnc-profiler, 还有动态Reload Class而不重启应用的JRebel。

使用方式 ,可以在启动命令里加入 -agentlib: 或 -agentpath: /path/to/agent.so,也可以用后面讲的VituralMachine.attach()动态加载。

1.2 Java Agent
Java Agent的底层也是JVMTI ,但后门能力就只剩一个AOP 代码植入了:在加载class文件之前做拦截并对字节码做修改。比如AspectJ,单元测试覆盖率的Jacoco,动态重载Class的Spring-Loaded。

典型代码如下:

public class MyAgent {
public static void premain(String args, Instrumentation instr){

<pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font: 400 12px/1.6 "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; overflow-wrap: break-word; margin-top: 0px; margin-bottom: 16px; padding: 10px 10px 10px 5px; overflow: auto; background: rgb(252, 252, 252); border-radius: 3px; border: 1px solid rgb(225, 225, 232); white-space: pre-wrap; color: rgb(51, 51, 51); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

  1. ClassFileTransformer transformer = new MyClassWeaving();
  2. instr.addTransformer(transformer);

</pre>

}
}

另一种逍遥的用法,就是为了随时让 “一段代码” 与 “主应用” 在同一JVM中运行,而不用修改主应用的代码去显式调用。

比如JMXAgent,启动一条TCP侦听线程响应JMX 请求。
比如jolokia的agent,启动一条Http侦听线程,响应Restful版的JMX请求。
比如btrace,也是启动一条TCP线程与btrace client通信,接收client发过来的脚本字节码,进行加载并输出结果。

它有两种启动方式:

一种是在启动时命令行加入 -javaagent:/path/to/agent.jar,根据agent.jar中的MANIFEST.MF文件中的Premain-Class定义,JVM找到相应的MyAgent类,调用其premain函数。

一种是通过后面讲的VM.attach()技术,在任意时刻由外部程序来灵活加载,调用其agentmain函数。

2.两种Attach
两种截然不同方式实现的Attach,本质上都是在跟踪程序与目标JVM之间建立一个沟通的管道,然后在跟踪程序使用特定的API去操作目标JVM。

2.1 Vitural Machine.attach()
跟踪程序通过Unix Domain Socket 与目标JVM的Attach Listener线程进行交互。 Socket 文件为/tmp/.java_pid$PID。

API 接口是com.sun.tools.attach.VirtualMachine 及其子类sun.tools.attach.HotSpotVirtualMachine,在tools.jar中,运行时需要依赖,可以做下面的事情:

dumpHeap: jmap -dump 效果

heapHisto: jmap -histo效果

threadDump: jstack效果

dataDump: kill-3 效果(jstack + jmap -heap)

loadAgent: 动态加载C/Java Agent

agentProperties: 获得已加载Agent的属性

sytemProperties: 获得System Properties

setFlag: 动态设置可写的JVM参数(但没几个是可写的)

printFlag: 打印JVM 参数的值

jcmd: 执行jcmd命令,具体能干啥见jcmd $PID help

可见, jmap, jmap,jcmd 们默认就是基于这个机制来做事情的。

VJTools的vjmxcli的特色功能,如果应用的启动脚本忘了设定启动JMX,可以根据PID attach到应用里,动态的把JMX Agent启动起来,然后通Agent属性获得本地连接地址,嗯, jconsole也是这么干的。

2.2 SA.attach()
著名的SA(Serviceability Agent),用于分析JVM运行时进程的Snapshot数据。Snapshot的意思,就是当SA 开始分析时,整个目标JVM是停顿下来不工作的,让SA可以从容读取进程的内存,直到断开后才会恢复。所以在生产上使用这类工具时,必须先摘除流量。

这个神奇的操作,主要是通过系统调用ptrace实现。ptrace会使内核暂停目标进程并将控制权交给跟踪程序,使跟踪程序得以察看目标进程的内存,详见ptrace的man,所以在容器环境下,需要打开ptrace的安全权限。

API的接口,一个是 sun.jvm.hotspot.HotSpotAgent 负责attach, sun.jvm.hotspot.runtime.VM负责操作,在sa-jdi.jar中。

VM类从内存二进制信息中提取出JVM内部数据结构,包括:

内存的getObjectHeap()/getUniverse()

线程的getThreads()

永久代内容的getSymbolTable(),getStringTable(), getSystemDictionary()

还有很厉害的读内部Native对象值的getTypeDataBase()

jstack,jmap 们默认用前面的VM.attach()模式,与目标JVM的Attach Listener线程通信, 但如果目标JVM已经半死不活,Attach Listener线程无力响应时,就可以增加-F 参数,转而使用SA.attach 模式,用ptrace去暴力接管进程, 详细代码见sun.tools.jmap.JMap。

看代码你还会发现,因为AttachListener支持的命令有限,所以jmap -heap 打印heap的统计信息时,也是以SA模式进去。

VJTools的vjmap是基于SA对jmap做了增强,能够独立各个分代中对象的数量大小统计,用以排除新生代对象的干扰,查找内存缓慢泄漏的问题,比如更直接找出Survior区里age>2的对象。

SA模式比VM模式做相同事情时要慢一截,非必需时不要用它。还有,如果跟踪程序被kill-9 非正常退出,没有执行中断SA,目标JVM就会一直暂停在那里,Linux下可以执行kill -18 $PID 发送SIGCONT信号重新激活进程。

  1. 两种Data
    3.1 JMX
    文章已太多,不再啰嗦。其中vjtools的vjtop,演示了如何不停顿JVM的获得线程的CPU占用、内存分配信息,获得某条繁忙进程的StackTrace看看它在忙些什么,值得一看。

3.2 PerfData
很多人不知道的一个机制,JVM其实每秒都会将自己的大量统计数据,写入到 /tmp/hsperfdata_username/pid 文件中。

用下面的指令可以感受下:

jcmd $PID PerfCounter.print
内容包括jvm的基本信息,内存,GC,线程数等等,还有一些JMX中没有暴露的数据,比如包含JVM中所有的停顿的SafePoint信息。

//线程的情况
java.threads.daemon=6
java.threads.live=7

// younggen的情况
sun.gc.generation.0.capacity=44695552
sun.gc.generation.0.maxCapacity=715784192
sun.gc.generation.0.minCapacity=44695552
….

jps,其实就是读取/tmp/hsperfdata_$username/ 目录下所有的文件。

jstat,同样是读取这个神秘的文件。一个很大的好处,就是它只默默读取文件,而不会像JMX那样要与应用程序交互,打扰应用程序的工作。

自己写代码也简单,使用 sun.management.counter.perf.PerfInstrumentation类,VJTools里的vjtop 从JMX,PerfData和/proc/pid 三处地方,综合打印JVM的概况和繁忙线程的情况,里面的PerfData类就是其使用的演示。

PerfData文件是mmap到内存中的,读写都很快,但每次写完还要更新磁盘上的文件元数据比如last modified time,如果遇上磁盘高IO,还是有概率造成JVM被锁定一段时间。所以我们通过-XX:+PerfDisableSharedMem禁止了perfdata的写入,不过现在又有点摇摆。

注意,perfdata 和 vm.attach 都需要在/tmp 目录读写文件,如果目标JVM的启动参数重新指定了临时文件目录,而跟踪程序依然去读取/tmp ,也会导致这些机制失效。

  1. 小结
    VJTools (https://github.com/vipshop/vjtools) 的三个工具,是两种Attach, 两种Data技术的使用样本,大家可以直接阅读源码来加深印象,再顺手点个Star。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容