arthas/site/docs/doc/retransform.md

147 lines
4.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# retransform
[`mc-retransform`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=command-mc-retransform)
::: tip
加载外部的`.class`文件retransform jvm 已加载的类。
:::
参考:[Instrumentation#retransformClasses](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html#retransformClasses-java.lang.Class...-)
## 使用参考
```bash
retransform /tmp/Test.class
retransform -l
retransform -d 1 # delete retransform entry
retransform --deleteAll # delete all retransform entries
retransform --classPattern demo.* # triger retransform classes
retransform -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class
retransform --classLoaderClass 'sun.misc.Launcher$AppClassLoader' /tmp/Test.class
```
## retransform 指定的 .class 文件
```bash
$ retransform /tmp/MathGame.class
retransform success, size: 1, classes:
demo.MathGame
```
加载指定的 .class 文件,然后解析出 class name再 retransform jvm 中已加载的对应的类。每加载一个 `.class` 文件,则会记录一个 retransform entry.
::: tip
如果多次执行 retransform 加载同一个 class 文件,则会有多条 retransform entry.
:::
## 查看 retransform entry
```bash
$ retransform -l
Id ClassName TransformCount LoaderHash LoaderClassName
1 demo.MathGame 1 null null
```
- TransformCount 统计在 ClassFileTransformer#transform 函数里尝试返回 entry 对应的 .class 文件的次数,但并不表明 transform 一定成功。
## 删除指定 retransform entry
需要指定 id
```bash
retransform -d 1
```
## 删除所有 retransform entry
```bash
retransform --deleteAll
```
## 显式触发 retransform
```bash
$ retransform --classPattern demo.MathGame
retransform success, size: 1, classes:
demo.MathGame
```
> 注意:对于同一个类,当存在多个 retransform entry 时,如果显式触发 retransform ,则最后添加的 entry 生效(id 最大的)。
## 消除 retransform 的影响
如果对某个类执行 retransform 之后,想消除影响,则需要:
- 删除这个类对应的 retransform entry
- 重新触发 retransform
::: tip
如果不清除掉所有的 retransform entry并重新触发 retransform ,则 arthas stop 时retransform 过的类仍然生效。
:::
## 结合 jad/mc 命令使用
```bash
jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java
mc /tmp/UserController.java -d /tmp
retransform /tmp/com/example/demo/arthas/user/UserController.class
```
- jad 命令反编译,然后可以用其它编译器,比如 vim 来修改源码
- mc 命令来内存编译修改过的代码
- 用 retransform 命令加载新的字节码
## 上传 .class 文件到服务器的技巧
使用`mc`命令来编译`jad`的反编译的代码有可能失败。可以在本地修改代码,编译好后再上传到服务器上。有的服务器不允许直接上传文件,可以使用`base64`命令来绕过。
1. 在本地先转换`.class`文件为 base64再保存为 result.txt
```bash
base64 < Test.class > result.txt
```
2. 到服务器上,新建并编辑`result.txt`,复制本地的内容,粘贴再保存
3. 把服务器上的 `result.txt`还原为`.class`
```
base64 -d < result.txt > Test.class
```
4. 用 md5 命令计算哈希值,校验是否一致
## retransform 的限制
- 不允许新增加 field/method
- 正在跑的函数,没有退出不能生效,比如下面新增加的`System.out.println`,只有`run()`函数里的会生效
```java
public class MathGame {
public static void main(String[] args) throws InterruptedException {
MathGame game = new MathGame();
while (true) {
game.run();
TimeUnit.SECONDS.sleep(1);
// 这个不生效,因为代码一直跑在 while里
System.out.println("in loop");
}
}
public void run() throws InterruptedException {
// 这个生效因为run()函数每次都可以完整结束
System.out.println("call run()");
try {
int number = random.nextInt();
List<Integer> primeFactors = primeFactors(number);
print(number, primeFactors);
} catch (Exception e) {
System.out.println(String.format("illegalArgumentCount:%3d, ", illegalArgumentCount) + e.getMessage());
}
}
}
```