watch/trace support maxMatch option. (#2385)

This commit is contained in:
pandaapo 2023-02-09 17:02:45 +08:00 committed by GitHub
parent 92be8bc9ee
commit 738e4896e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 210 additions and 9 deletions

View File

@ -408,20 +408,20 @@ public class Enhancer implements ClassFileTransformer {
* 对象增强
*
* @param inst inst
* @param adviceId 通知ID
* @param isTracing 可跟踪方法调用
* @param skipJDKTrace 是否忽略对JDK内部方法的跟踪
* @param classNameMatcher 类名匹配
* @param methodNameMatcher 方法名匹配
* @param maxNumOfMatchedClass 匹配的class最大数量
* @return 增强影响范围
* @throws UnmodifiableClassException 增强失败
*/
public synchronized EnhancerAffect enhance(final Instrumentation inst) throws UnmodifiableClassException {
public synchronized EnhancerAffect enhance(final Instrumentation inst, int maxNumOfMatchedClass) throws UnmodifiableClassException {
// 获取需要增强的类集合
this.matchingClasses = GlobalOptions.isDisableSubClass
? SearchUtils.searchClass(inst, classNameMatcher)
: SearchUtils.searchSubClass(inst, SearchUtils.searchClass(inst, classNameMatcher));
if (matchingClasses.size() > maxNumOfMatchedClass) {
affect.setOverLimitMsg("The number of matched classes is " +matchingClasses.size()+ ", greater than the limit value " + maxNumOfMatchedClass + ". Try to change the limit with option '-m <arg>'.");
return affect;
}
// 过滤掉无法被增强的类
List<Pair<Class<?>, String>> filtedList = filter(matchingClasses);
if (!filtedList.isEmpty()) {

View File

@ -20,6 +20,7 @@ public class EnhancerAffectVO {
private Throwable throwable;
private List<String> classDumpFiles;
private List<String> methods;
private String overLimitMsg;
public EnhancerAffectVO(EnhancerAffect affect) {
this.cost = affect.cost();
@ -27,6 +28,7 @@ public class EnhancerAffectVO {
this.methodCount = affect.mCnt();
this.listenerId = affect.getListenerId();
this.throwable = affect.getThrowable();
this.overLimitMsg = affect.getOverLimitMsg();
if (GlobalOptions.isDump) {
classDumpFiles = new ArrayList<String>();
@ -87,4 +89,12 @@ public class EnhancerAffectVO {
public void setMethods(List<String> methods) {
this.methods = methods;
}
public void setOverLimitMsg(String overLimitMsg) {
this.overLimitMsg = overLimitMsg;
}
public String getOverLimitMsg() {
return overLimitMsg;
}
}

View File

@ -20,9 +20,11 @@ import com.taobao.arthas.core.shell.handlers.shell.QExitHandler;
import com.taobao.arthas.core.shell.session.Session;
import com.taobao.arthas.core.util.Constants;
import com.taobao.arthas.core.util.LogUtil;
import com.taobao.arthas.core.util.StringUtils;
import com.taobao.arthas.core.util.affect.EnhancerAffect;
import com.taobao.arthas.core.util.matcher.Matcher;
import com.taobao.arthas.core.view.Ansi;
import com.taobao.middleware.cli.annotations.DefaultValue;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Option;
@ -45,6 +47,8 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
protected boolean verbose;
protected int maxNumOfMatchedClass;
@Option(longName = "exclude-class-pattern")
@Description("exclude class name pattern, use either '.' or '/' as separator")
public void setExcludeClassPattern(String excludeClassPattern) {
@ -63,6 +67,13 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
this.verbose = verbose;
}
@Option(shortName = "m", longName = "maxMatch")
@DefaultValue("50")
@Description("The maximum of matched class.")
public void setMaxNumOfMatchedClass(int maxNumOfMatchedClass) {
this.maxNumOfMatchedClass = maxNumOfMatchedClass;
}
/**
* 类名匹配
*
@ -159,7 +170,7 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
Enhancer enhancer = new Enhancer(listener, listener instanceof InvokeTraceable, skipJDKTrace, getClassNameMatcher(), getClassNameExcludeMatcher(), getMethodNameMatcher());
// 注册通知监听器
process.register(listener, enhancer);
effect = enhancer.enhance(inst);
effect = enhancer.enhance(inst, this.maxNumOfMatchedClass);
if (effect.getThrowable() != null) {
String msg = "error happens when enhancing class: "+effect.getThrowable().getMessage();
@ -170,6 +181,11 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
if (effect.cCnt() == 0 || effect.mCnt() == 0) {
// no class effected
if (!StringUtils.isEmpty(effect.getOverLimitMsg())) {
process.appendResult(new EnhancerModel(effect, false));
process.end(-1);
return;
}
// might be method code too large
process.appendResult(new EnhancerModel(effect, false, "No class or method is affected"));

View File

@ -95,7 +95,9 @@ public class ViewRenderUtil {
affectVO.getMethodCount(),
affectVO.getCost(),
affectVO.getListenerId()));
if (!StringUtils.isEmpty(affectVO.getOverLimitMsg())) {
infoSB.append("\n" + affectVO.getOverLimitMsg());
}
if (affectVO.getThrowable() != null) {
infoSB.append("\nEnhance error! exception: ").append(affectVO.getThrowable());
}

View File

@ -123,7 +123,6 @@ public class SearchUtils {
return matches;
}
/**
* 搜索目标类的内部类
*

View File

@ -34,6 +34,8 @@ public final class EnhancerAffect extends Affect {
private final List<String> methods = new ArrayList<String>();
private String overLimitMsg;
public EnhancerAffect() {
}
@ -121,6 +123,14 @@ public final class EnhancerAffect extends Affect {
return methods;
}
public String getOverLimitMsg() {
return overLimitMsg;
}
public void setOverLimitMsg(String overLimitMsg) {
this.overLimitMsg = overLimitMsg;
}
@Override
public String toString() {
//TODO removing EnhancerAffect.toString(), replace with ViewRenderUtil.renderEnhancerAffect()

View File

@ -39,6 +39,7 @@
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
| `[c:]` | 统计周期,默认值为 120 秒 |
| [b] | 在**方法调用之前**计算 condition-express |
|`[m <arg>]` | 指定Class最大匹配数量默认值为50。长格式为`[maxMatch <arg>]`。 |
## 使用参考
@ -71,6 +72,21 @@ Affect(class-cnt:1 , method-cnt:1) cost in 94 ms.
2018-12-03 19:07:03 demo.MathGame primeFactors 2 2 0 3182.72 0.00%
```
### 指定Class最大匹配数量
```bash
$ monitor -c 1 -m 1 demo.MathGame primeFactors
Press Q or Ctrl+C to abort.
Affect(class count:1 , method count:1) cost in 384 ms, listenerId: 6.
timestamp class method total success fail avg-rt(ms) fail-rate
-----------------------------------------------------------------------------------------------
2022-12-25 21:12:58 demo.MathGame primeFactors 1 1 0 0.18 0.00%
timestamp class method total success fail avg-rt(ms) fail-rate
-----------------------------------------------------------------------------------------------
2022-12-25 21:12:59 demo.MathGame primeFactors 0 0 0 0.00 0.00%
```
### 计算条件表达式过滤统计结果(方法执行完毕之后)
```bash

View File

@ -17,6 +17,7 @@
| _condition-express_ | 条件表达式 |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
| `[n:]` | 执行次数限制 |
|`[m <arg>]` | 指定Class最大匹配数量默认值为50。长格式为`[maxMatch <arg>]`。 |
这里重点要说明的是观察表达式,观察表达式的构成主要由 ognl 表达式组成,所以你可以这样写`"{params,returnObj}"`,只要是一个合法的 ognl 表达式,都能被正常支持。
@ -44,6 +45,18 @@ ts=2018-12-04 01:32:19;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun
at demo.MathGame.main(MathGame.java:16)
```
### 指定Class最大匹配数量
```bash
$ stack demo.MathGame primeFactors -m 1
Press Q or Ctrl+C to abort.
Affect(class count:1 , method count:1) cost in 561 ms, listenerId: 5.
ts=2022-12-25 21:07:07;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@b4aac2
@demo.MathGame.primeFactors()
at demo.MathGame.run(MathGame.java:46)
at demo.MathGame.main(MathGame.java:38)
```
### 据条件表达式来过滤
```bash

View File

@ -18,6 +18,7 @@
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
| `[n:]` | 命令执行次数 |
| `#cost` | 方法执行耗时 |
|`[m <arg>]` | 指定Class最大匹配数量默认值为50。长格式为`[maxMatch <arg>]`。 |
这里重点要说明的是观察表达式,观察表达式的构成主要由 ognl 表达式组成,所以你可以这样写`"{params,returnObj}"`,只要是一个合法的 ognl 表达式,都能被正常支持。
@ -69,6 +70,21 @@ Affect(class-cnt:1 , method-cnt:1) cost in 28 ms.
结果里的 `#24`,表示在 run 函数里,在源文件的第`24`行调用了`primeFactors()`函数。
:::
### 指定Class匹配的最大数量
```bash
$ trace demo.MathGame run -m 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 412 ms, listenerId: 4
`---ts=2022-12-25 21:00:00;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@b4aac2
`---[0.762093ms] demo.MathGame:run()
`---[30.21% 0.230241ms] demo.MathGame:primeFactors() #46 [throws Exception]
`---ts=2022-12-25 21:00:10;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@b4aac2
`---[0.315298ms] demo.MathGame:run()
`---[13.95% 0.043995ms] demo.MathGame:primeFactors() #46 [throws Exception]
```
### trace 次数限制
如果方法调用的次数很多,那么可以用`-n`参数指定捕捉结果的次数。比如下面的例子里,捕捉到一次调用就退出命令。

View File

@ -35,6 +35,18 @@ Affect(class-cnt:1 , method-cnt:1) cost in 66 ms.
1004 2018-12-04 11:15:42 17.76437 true false 0x4b67cf4d MathGame primeFactors
```
### 指定Class最大匹配数量
```bash
$ tt -t -m 1 demo.MathGame primeFactors
Press Q or Ctrl+C to abort.
Affect(class count:1 , method count:1) cost in 130 ms, listenerId: 1.
INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
-------------------------------------------------------------------------------------------------------------------------------------
1000 2022-12-25 19:41:45 2.629929 true false 0x3bf400 MathGame primeFactors
1001 2022-12-25 19:41:55 0.146161 false true 0x3bf400 MathGame primeFactors
```
- 命令参数解析
- `-t`
@ -46,6 +58,10 @@ Affect(class-cnt:1 , method-cnt:1) cost in 66 ms.
当你执行一个调用量不高的方法时可能你还能有足够的时间用 `CTRL+C` 中断 tt 命令记录的过程,但如果遇到调用量非常大的方法,瞬间就能将你的 JVM 内存撑爆。
此时你可以通过 `-n` 参数指定你需要记录的次数,当达到记录次数时 Arthas 会主动中断 tt 命令的记录过程,避免人工操作无法停止的情况。
- `-m 1`
通过 `-m` 参数指定Class匹配的最大数量防止匹配到的Class数量太多导致JVM挂起默认值是50。
- 表格字段说明

View File

@ -24,6 +24,7 @@ watch 的参数比较多,主要是因为它能在 4 个不同的场景观察
| [f] | 在**函数结束之后**(正常返回和异常返回)观察 |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
| [x:] | 指定输出结果的属性遍历深度,默认为 1最大值是 4 |
|`[m <arg>]` | 指定Class最大匹配数量默认值为50。长格式为`[maxMatch <arg>]`。 |
这里重点要说明的是观察表达式,观察表达式的构成主要由 ognl 表达式组成,所以你可以这样写`"{params,returnObj}"`,只要是一个合法的 ognl 表达式,都能被正常支持。
@ -87,6 +88,26 @@ ts=2021-08-31 15:22:58; [cost=1.020982ms] result=@ArrayList[
- 上面的结果里,说明函数被执行了两次,第一次结果是`location=AtExceptionExit`,说明函数抛出异常了,因此`returnObj`是 null
- 在第二次结果里是`location=AtExit`,说明函数正常返回,因此可以看到`returnObj`结果是一个 ArrayList
### 指定Class最大匹配数量
```bash
$ watch demo.MathGame primeFactors -m 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 302 ms, listenerId: 3
method=demo.MathGame.primeFactors location=AtExceptionExit
ts=2022-12-25 19:58:41; [cost=0.222419ms] result=@ArrayList[
@Object[][isEmpty=false;size=1],
@MathGame[demo.MathGame@3bf400],
null,
]
method=demo.MathGame.primeFactors location=AtExceptionExit
ts=2022-12-25 19:58:51; [cost=0.046928ms] result=@ArrayList[
@Object[][isEmpty=false;size=1],
@MathGame[demo.MathGame@3bf400],
null,
]
```
### 观察函数调用入口的参数和返回值
```bash

View File

@ -39,6 +39,7 @@ Parameter `[c:]` stands for cycles of statistics. Its value is an integer value
| `[E]` | turn on regex matching while the default is wildcard matching |
| `[c:]` | cycle of statistics, the default value: `120`s |
| `[b]` | evaluate the condition-expression before method invoke |
|`[m <arg>]` | Specify the max number of matched Classes, the default value is 50. Long format is `[maxMatch <arg>]`. |
## Usage
@ -71,6 +72,21 @@ Affect(class-cnt:1 , method-cnt:1) cost in 94 ms.
2018-12-03 19:07:03 demo.MathGame primeFactors 2 2 0 3182.72 0.00%
```
### Specify the max number of matched Classes
```bash
$ monitor -c 1 -m 1 demo.MathGame primeFactors
Press Q or Ctrl+C to abort.
Affect(class count:1 , method count:1) cost in 384 ms, listenerId: 6.
timestamp class method total success fail avg-rt(ms) fail-rate
-----------------------------------------------------------------------------------------------
2022-12-25 21:12:58 demo.MathGame primeFactors 1 1 0 0.18 0.00%
timestamp class method total success fail avg-rt(ms) fail-rate
-----------------------------------------------------------------------------------------------
2022-12-25 21:12:59 demo.MathGame primeFactors 0 0 0 0.00 0.00%
```
### Evaluate condition-express to filter method (after method call)
```bash

View File

@ -17,6 +17,7 @@ Most often we know one method gets called, but we have no idea on which code pat
| _condition-expression_ | condition expression |
| `[E]` | turn on regex match, the default behavior is wildcard match |
| `[n:]` | execution times |
|`[m <arg>]` | Specify the max number of matched Classes, the default value is 50. Long format is `[maxMatch <arg>]`. |
There's one thing worthy noting here is observation expression. The observation expression supports OGNL grammar, for example, you can come up a expression like this `"{params,returnObj}"`. All OGNL expressions are supported as long as they are legal to the grammar.
@ -44,6 +45,18 @@ ts=2018-12-04 01:32:19;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun
at demo.MathGame.main(MathGame.java:16)
```
### Specify the max number of matched Classes
```bash
$ stack demo.MathGame primeFactors -m 1
Press Q or Ctrl+C to abort.
Affect(class count:1 , method count:1) cost in 561 ms, listenerId: 5.
ts=2022-12-25 21:07:07;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@b4aac2
@demo.MathGame.primeFactors()
at demo.MathGame.run(MathGame.java:46)
at demo.MathGame.main(MathGame.java:38)
```
### Filtering by condition expression
```bash

View File

@ -18,6 +18,7 @@ Trace method calling path, and output the time cost for each node in the path.
| `[E]` | enable regex match, the default behavior is wildcards match |
| `[n:]` | execution times |
| #cost | time cost |
|`[m <arg>]` | Specify the max number of matched Classes, the default value is 50. Long format is `[maxMatch <arg>]`. |
There's one thing worthy noting here is observation expression. The observation expression supports OGNL grammar, for example, you can come up a expression like this `"{params,returnObj}"`. All OGNL expressions are supported as long as they are legal to the grammar.
@ -67,6 +68,21 @@ Affect(class-cnt:1 , method-cnt:1) cost in 28 ms.
The `#24` in the result indicates that in the run function, the `primeFactors()` function was called on line `24` of the source file.
:::
### Specify the max number of matched Classes
```bash
$ trace demo.MathGame run -m 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 412 ms, listenerId: 4
`---ts=2022-12-25 21:00:00;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@b4aac2
`---[0.762093ms] demo.MathGame:run()
`---[30.21% 0.230241ms] demo.MathGame:primeFactors() #46 [throws Exception]
`---ts=2022-12-25 21:00:10;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@b4aac2
`---[0.315298ms] demo.MathGame:run()
`---[13.95% 0.043995ms] demo.MathGame:primeFactors() #46 [throws Exception]
```
### Trace times limit
If the method invoked many times, use `-n` options to specify trace times. For example, the command will exit when received a trace result.

View File

@ -31,6 +31,18 @@ Affect(class-cnt:1 , method-cnt:1) cost in 66 ms.
1004 2018-12-04 11:15:42 17.76437 true false 0x4b67cf4d MathGame primeFactors
```
### Specify the max number of matched Classes
```bash
$ tt -t -m 1 demo.MathGame primeFactors
Press Q or Ctrl+C to abort.
Affect(class count:1 , method count:1) cost in 130 ms, listenerId: 1.
INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
-------------------------------------------------------------------------------------------------------------------------------------
1000 2022-12-25 19:41:45 2.629929 true false 0x3bf400 MathGame primeFactors
1001 2022-12-25 19:41:55 0.146161 false true 0x3bf400 MathGame primeFactors
```
- `-t`
record the calling context of the method `demo.MathGame primeFactors`
@ -39,6 +51,10 @@ Affect(class-cnt:1 , method-cnt:1) cost in 66 ms.
limit the number of the records (avoid overflow for too many records; with `-n` option, Arthas can automatically stop recording once the records reach the specified limit)
- `-m 1`
limit the number of matched Classes to avoid JVM suspending when too many matched Classes. The default value is 50.
- Property
| Name | Specification |

View File

@ -22,6 +22,7 @@ There are four different scenarios for `watch` command, which makes it rather co
| [f] | when method exits (either succeed or fail with exceptions) |
| [E] | turn on regex matching while the default is wildcard matching |
| [x:] | the depth to print the specified property with default value: 1, the max value is 4 |
|`[m <arg>]` | Specify the max number of matched Classes, the default value is 50. Long format is `[maxMatch <arg>]`. |
F.Y.I
@ -85,6 +86,26 @@ ts=2021-08-31 15:22:58; [cost=1.020982ms] result=@ArrayList[
- In the above result, the method is executed twice, the first result is `location=AtExceptionExit`, indicating that the method throws an exception, so `returnObj` is null
- In the second result is `location=AtExit`, indicating that the method returns normally, so you can see that the result of `returnObj` is an ArrayList
### Specify the max number of matched Classes
```bash
$ watch demo.MathGame primeFactors -m 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 302 ms, listenerId: 3
method=demo.MathGame.primeFactors location=AtExceptionExit
ts=2022-12-25 19:58:41; [cost=0.222419ms] result=@ArrayList[
@Object[][isEmpty=false;size=1],
@MathGame[demo.MathGame@3bf400],
null,
]
method=demo.MathGame.primeFactors location=AtExceptionExit
ts=2022-12-25 19:58:51; [cost=0.046928ms] result=@ArrayList[
@Object[][isEmpty=false;size=1],
@MathGame[demo.MathGame@3bf400],
null,
]
```
### Check `in parameters`
```bash