|
|
|
@ -1,9 +1,13 @@
|
|
|
|
|
package com.taobao.arthas.core.command.monitor200;
|
|
|
|
|
|
|
|
|
|
import java.lang.instrument.Instrumentation;
|
|
|
|
|
import java.lang.instrument.UnmodifiableClassException;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
import com.taobao.arthas.core.advisor.AdviceListener;
|
|
|
|
|
import com.taobao.arthas.core.advisor.Enhancer;
|
|
|
|
|
import com.taobao.arthas.core.advisor.InvokeTraceable;
|
|
|
|
|
import com.taobao.arthas.core.shell.cli.CliToken;
|
|
|
|
|
import com.taobao.arthas.core.shell.cli.Completion;
|
|
|
|
|
import com.taobao.arthas.core.shell.cli.CompletionUtils;
|
|
|
|
|
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
|
|
|
|
@ -12,33 +16,18 @@ import com.taobao.arthas.core.shell.handlers.command.CommandInterruptHandler;
|
|
|
|
|
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.SearchUtils;
|
|
|
|
|
import com.taobao.arthas.core.util.affect.EnhancerAffect;
|
|
|
|
|
import com.taobao.arthas.core.util.matcher.Matcher;
|
|
|
|
|
import com.taobao.middleware.cli.CLI;
|
|
|
|
|
import com.taobao.middleware.cli.annotations.CLIConfigurator;
|
|
|
|
|
import com.taobao.middleware.logger.Logger;
|
|
|
|
|
|
|
|
|
|
import java.lang.instrument.Instrumentation;
|
|
|
|
|
import java.lang.instrument.UnmodifiableClassException;
|
|
|
|
|
import java.lang.reflect.Method;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.Iterator;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @author beiwei30 on 29/11/2016.
|
|
|
|
|
*/
|
|
|
|
|
public abstract class EnhancerCommand extends AnnotatedCommand {
|
|
|
|
|
|
|
|
|
|
private static final Logger logger = LogUtil.getArthasLogger();
|
|
|
|
|
private static final int SIZE_LIMIT = 50;
|
|
|
|
|
private static final int MINIMAL_COMPLETE_SIZE = 3;
|
|
|
|
|
protected static final List<String> EMPTY = Collections.emptyList();
|
|
|
|
|
private static final String[] EXPRESS_EXAMPLES = { "params", "returnObj", "throwExp", "target", "clazz", "method",
|
|
|
|
|
public static final String[] EXPRESS_EXAMPLES = { "params", "returnObj", "throwExp", "target", "clazz", "method",
|
|
|
|
|
"{params,returnObj}", "params[0]" };
|
|
|
|
|
|
|
|
|
|
protected Matcher classNameMatcher;
|
|
|
|
@ -76,39 +65,24 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void complete(Completion completion) {
|
|
|
|
|
List<CliToken> tokens = completion.lineTokens();
|
|
|
|
|
CliToken lastToken = tokens.get(tokens.size() - 1);
|
|
|
|
|
int argumentIndex = CompletionUtils.detectArgumentIndex(completion);
|
|
|
|
|
|
|
|
|
|
CompleteContext completeContext = getCompleteContext(completion);
|
|
|
|
|
if (completeContext == null) {
|
|
|
|
|
completeDefault(completion, lastToken);
|
|
|
|
|
if (argumentIndex == 1) { // class name
|
|
|
|
|
if (!CompletionUtils.completeClassName(completion)) {
|
|
|
|
|
super.complete(completion);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
} else if (argumentIndex == 2) { // method name
|
|
|
|
|
if (!CompletionUtils.completeMethodName(completion)) {
|
|
|
|
|
super.complete(completion);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
} else if (argumentIndex == 3) { // watch express
|
|
|
|
|
completeArgument3(completion);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (completeContext.getState()) {
|
|
|
|
|
case INIT:
|
|
|
|
|
if (completeClassName(completion)) {
|
|
|
|
|
completeContext.setState(CompleteContext.CompleteState.CLASS_COMPLETED);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case CLASS_COMPLETED:
|
|
|
|
|
if (completeMethodName(completion)) {
|
|
|
|
|
completeContext.setState(CompleteContext.CompleteState.METHOD_COMPLETED);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case METHOD_COMPLETED:
|
|
|
|
|
if (completeExpress(completion)) {
|
|
|
|
|
completeContext.setState(CompleteContext.CompleteState.EXPRESS_COMPLETED);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case EXPRESS_COMPLETED:
|
|
|
|
|
if (completeConditionExpress(completion)) {
|
|
|
|
|
completeContext.setState(CompleteContext.CompleteState.CONDITION_EXPRESS_COMPLETED);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case CONDITION_EXPRESS_COMPLETED:
|
|
|
|
|
completion.complete(EMPTY);
|
|
|
|
|
}
|
|
|
|
|
super.complete(completion);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void enhance(CommandProcess process) {
|
|
|
|
@ -166,138 +140,8 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return true if the class name is successfully completed
|
|
|
|
|
*/
|
|
|
|
|
protected boolean completeClassName(Completion completion) {
|
|
|
|
|
CliToken lastToken = completion.lineTokens().get(completion.lineTokens().size() - 1);
|
|
|
|
|
if (lastToken.value().length() >= MINIMAL_COMPLETE_SIZE) {
|
|
|
|
|
// complete class name
|
|
|
|
|
Set<Class<?>> results = SearchUtils.searchClassOnly(completion.session().getInstrumentation(),
|
|
|
|
|
"*" + lastToken.value() + "*", SIZE_LIMIT);
|
|
|
|
|
if (results.size() >= SIZE_LIMIT) {
|
|
|
|
|
Iterator<Class<?>> it = results.iterator();
|
|
|
|
|
List<String> res = new ArrayList<String>(SIZE_LIMIT);
|
|
|
|
|
while (it.hasNext()) {
|
|
|
|
|
res.add(it.next().getName());
|
|
|
|
|
}
|
|
|
|
|
res.add("and possibly more...");
|
|
|
|
|
completion.complete(res);
|
|
|
|
|
} else if (results.size() == 1) {
|
|
|
|
|
Class<?> clazz = results.iterator().next();
|
|
|
|
|
completion.complete(clazz.getName().substring(lastToken.value().length()), true);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
List<String> res = new ArrayList<String>(results.size());
|
|
|
|
|
for (Class clazz : results) {
|
|
|
|
|
res.add(clazz.getName());
|
|
|
|
|
}
|
|
|
|
|
completion.complete(res);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// forget to call completion.complete will cause terminal to stuck.
|
|
|
|
|
completion.complete(Collections.singletonList("Too many classes to display, "
|
|
|
|
|
+ "please try to input at least 3 characters to get auto complete working."));
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected boolean completeMethodName(Completion completion) {
|
|
|
|
|
List<CliToken> tokens = completion.lineTokens();
|
|
|
|
|
CliToken lastToken = completion.lineTokens().get(tokens.size() - 1);
|
|
|
|
|
|
|
|
|
|
// retrieve the class name
|
|
|
|
|
String className;
|
|
|
|
|
if (" ".equals(lastToken.value())) {
|
|
|
|
|
// tokens = { " ", "CLASS_NAME", " "}
|
|
|
|
|
className = tokens.get(tokens.size() - 2).value();
|
|
|
|
|
} else {
|
|
|
|
|
// tokens = { " ", "CLASS_NAME", " ", "PARTIAL_METHOD_NAME"}
|
|
|
|
|
className = tokens.get(tokens.size() - 3).value();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Set<Class<?>> results = SearchUtils.searchClassOnly(completion.session().getInstrumentation(), className, 2);
|
|
|
|
|
if (results.isEmpty() || results.size() > 1) {
|
|
|
|
|
// no class found or multiple class found
|
|
|
|
|
completion.complete(EMPTY);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Class<?> clazz = results.iterator().next();
|
|
|
|
|
|
|
|
|
|
List<String> res = new ArrayList<String>();
|
|
|
|
|
|
|
|
|
|
for (Method method : clazz.getDeclaredMethods()) {
|
|
|
|
|
if (" ".equals(lastToken.value())) {
|
|
|
|
|
res.add(method.getName());
|
|
|
|
|
} else if (method.getName().contains(lastToken.value())) {
|
|
|
|
|
res.add(method.getName());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (res.size() == 1) {
|
|
|
|
|
completion.complete(res.get(0).substring(lastToken.value().length()), true);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
completion.complete(res);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected boolean completeExpress(Completion completion) {
|
|
|
|
|
return CompletionUtils.complete(completion, Arrays.asList(EXPRESS_EXAMPLES));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected boolean completeConditionExpress(Completion completion) {
|
|
|
|
|
completion.complete(EMPTY);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void completeDefault(Completion completion, CliToken lastToken) {
|
|
|
|
|
CLI cli = CLIConfigurator.define(this.getClass());
|
|
|
|
|
List<com.taobao.middleware.cli.Option> options = cli.getOptions();
|
|
|
|
|
if (lastToken == null || lastToken.isBlank()) {
|
|
|
|
|
// complete usage
|
|
|
|
|
CompletionUtils.completeUsage(completion, cli);
|
|
|
|
|
} else if (lastToken.value().startsWith("--")) {
|
|
|
|
|
// complete long option
|
|
|
|
|
CompletionUtils.completeLongOption(completion, lastToken, options);
|
|
|
|
|
} else if (lastToken.value().startsWith("-")) {
|
|
|
|
|
// complete short option
|
|
|
|
|
CompletionUtils.completeShortOption(completion, lastToken, options);
|
|
|
|
|
} else {
|
|
|
|
|
completion.complete(EMPTY);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private CompleteContext getCompleteContext(Completion completion) {
|
|
|
|
|
CompleteContext completeContext = new CompleteContext();
|
|
|
|
|
List<CliToken> tokens = completion.lineTokens();
|
|
|
|
|
CliToken lastToken = tokens.get(tokens.size() - 1);
|
|
|
|
|
|
|
|
|
|
if (lastToken.value().startsWith("-") || lastToken.value().startsWith("--")) {
|
|
|
|
|
// this is the default case
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int tokenCount = 0;
|
|
|
|
|
|
|
|
|
|
for (CliToken token : tokens) {
|
|
|
|
|
if (" ".equals(token.value()) || token.value().startsWith("-") || token.value().startsWith("--")) {
|
|
|
|
|
// filter irrelevant tokens
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
tokenCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (CompleteContext.CompleteState state : CompleteContext.CompleteState.values()) {
|
|
|
|
|
if (tokenCount == state.ordinal() || tokenCount == state.ordinal() + 1 && !" ".equals(lastToken.value())) {
|
|
|
|
|
completeContext.setState(state);
|
|
|
|
|
return completeContext;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return completeContext;
|
|
|
|
|
protected void completeArgument3(Completion completion) {
|
|
|
|
|
super.complete(completion);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void warn(CommandProcess process, String message) {
|
|
|
|
|