mirror of
https://github.com/alibaba/arthas.git
synced 2024-04-21 10:21:39 +00:00
watch/monitor/trace/tt/stack support better auto completion. close #497
This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
/**
|
||||
* @author ralf0131 2017-01-11 14:57.
|
||||
*/
|
||||
public class CompleteContext {
|
||||
|
||||
private CompleteState state;
|
||||
|
||||
public CompleteContext() {
|
||||
this.state = CompleteState.INIT;
|
||||
}
|
||||
|
||||
public void setState(CompleteState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public CompleteState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* The state transition diagram is:
|
||||
* INIT -> CLASS_NAME -> METHOD_NAME -> FINISHED
|
||||
*/
|
||||
enum CompleteState {
|
||||
|
||||
/**
|
||||
* the state that nothing is completed
|
||||
*/
|
||||
INIT,
|
||||
|
||||
/**
|
||||
* the state that class name is completed
|
||||
*/
|
||||
CLASS_COMPLETED,
|
||||
|
||||
/**
|
||||
* the state that method name is completed
|
||||
*/
|
||||
METHOD_COMPLETED,
|
||||
|
||||
/**
|
||||
* the state that express is completed
|
||||
*/
|
||||
EXPRESS_COMPLETED,
|
||||
|
||||
/**
|
||||
* the state that condition-express is completed
|
||||
*/
|
||||
CONDITION_EXPRESS_COMPLETED
|
||||
}
|
||||
}
|
||||
+22
-178
@@ -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) {
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
import com.taobao.arthas.core.advisor.AdviceListener;
|
||||
import com.taobao.arthas.core.command.Constants;
|
||||
import com.taobao.arthas.core.shell.cli.Completion;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.shell.handlers.Handler;
|
||||
import com.taobao.arthas.core.util.SearchUtils;
|
||||
@@ -119,10 +118,4 @@ public class MonitorCommand extends EnhancerCommand {
|
||||
});
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean completeExpress(Completion completion) {
|
||||
completion.complete(EMPTY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
import com.taobao.arthas.core.advisor.AdviceListener;
|
||||
import com.taobao.arthas.core.command.Constants;
|
||||
import com.taobao.arthas.core.shell.cli.Completion;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.util.SearchUtils;
|
||||
import com.taobao.arthas.core.util.matcher.Matcher;
|
||||
@@ -106,9 +105,4 @@ public class StackCommand extends EnhancerCommand {
|
||||
return new StackAdviceListener(this, process);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean completeExpress(Completion completion) {
|
||||
completion.complete(EMPTY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.taobao.arthas.core.advisor.AdviceListener;
|
||||
import com.taobao.arthas.core.command.Constants;
|
||||
import com.taobao.arthas.core.command.express.ExpressException;
|
||||
import com.taobao.arthas.core.command.express.ExpressFactory;
|
||||
import com.taobao.arthas.core.shell.cli.Completion;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.advisor.Advice;
|
||||
import com.taobao.arthas.core.advisor.ArthasMethod;
|
||||
@@ -285,12 +284,6 @@ public class TimeTunnelCommand extends EnhancerCommand {
|
||||
return new TimeTunnelAdviceListener(this, process);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean completeExpress(Completion completion) {
|
||||
completion.complete(EMPTY);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 展示指定记录
|
||||
private void processShow(CommandProcess process) {
|
||||
RowAffect affect = new RowAffect();
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
import com.taobao.arthas.core.advisor.AdviceListener;
|
||||
import com.taobao.arthas.core.command.Constants;
|
||||
import com.taobao.arthas.core.shell.cli.Completion;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.util.SearchUtils;
|
||||
import com.taobao.arthas.core.util.matcher.GroupMatcher;
|
||||
@@ -148,12 +147,6 @@ public class TraceCommand extends EnhancerCommand {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean completeExpress(Completion completion) {
|
||||
completion.complete(EMPTY);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造追踪路径匹配
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.taobao.arthas.core.advisor.AdviceListener;
|
||||
import com.taobao.arthas.core.command.Constants;
|
||||
import com.taobao.arthas.core.shell.cli.Completion;
|
||||
import com.taobao.arthas.core.shell.cli.CompletionUtils;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.util.SearchUtils;
|
||||
import com.taobao.arthas.core.util.matcher.Matcher;
|
||||
@@ -14,12 +18,12 @@ import com.taobao.middleware.cli.annotations.Summary;
|
||||
@Name("watch")
|
||||
@Summary("Display the input/output parameter, return object, and thrown exception of specified method invocation")
|
||||
@Description(Constants.EXPRESS_DESCRIPTION + "\nExamples:\n" +
|
||||
" watch -b org.apache.commons.lang.StringUtils isBlank params[0]\n" +
|
||||
" watch -b org.apache.commons.lang.StringUtils isBlank params\n" +
|
||||
" watch -f org.apache.commons.lang.StringUtils isBlank returnObj\n" +
|
||||
" watch -bf *StringUtils isBlank params[0]\n" +
|
||||
" watch -bf *StringUtils isBlank params\n" +
|
||||
" watch *StringUtils isBlank params[0]\n" +
|
||||
" watch *StringUtils isBlank params[0] params[0].length==1\n" +
|
||||
" watch *StringUtils isBlank '#cost>100'\n" +
|
||||
" watch *StringUtils isBlank params '#cost>100'\n" +
|
||||
" watch -E -b org\\.apache\\.commons\\.lang\\.StringUtils isBlank params[0]\n" +
|
||||
Constants.WIKI + Constants.WIKI_HOME + "watch")
|
||||
public class WatchCommand extends EnhancerCommand {
|
||||
@@ -177,4 +181,9 @@ public class WatchCommand extends EnhancerCommand {
|
||||
protected AdviceListener getAdviceListener(CommandProcess process) {
|
||||
return new WatchAdviceListener(this, process);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void completeArgument3(Completion completion) {
|
||||
CompletionUtils.complete(completion, Arrays.asList(EXPRESS_EXAMPLES));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,9 +156,13 @@ public class CompletionUtils {
|
||||
|
||||
public static boolean completeClassName(Completion completion) {
|
||||
List<CliToken> tokens = completion.lineTokens();
|
||||
String token = tokens.get(tokens.size() - 1).value();
|
||||
String lastToken = tokens.get(tokens.size() - 1).value();
|
||||
|
||||
if (token.startsWith("-") || StringUtils.isBlank(token)) {
|
||||
if (StringUtils.isBlank(lastToken)) {
|
||||
lastToken = "";
|
||||
}
|
||||
|
||||
if (lastToken.startsWith("-")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -169,8 +173,11 @@ public class CompletionUtils {
|
||||
Set<String> result = new HashSet<String>();
|
||||
for(Class<?> clazz : allLoadedClasses) {
|
||||
String name = clazz.getName();
|
||||
if(name.startsWith(token)) {
|
||||
int index = name.indexOf('.', token.length());
|
||||
if (name.startsWith("[")) {
|
||||
continue;
|
||||
}
|
||||
if(name.startsWith(lastToken)) {
|
||||
int index = name.indexOf('.', lastToken.length());
|
||||
|
||||
if(index > 0) {
|
||||
result.add(name.substring(0, index + 1));
|
||||
@@ -182,7 +189,7 @@ public class CompletionUtils {
|
||||
}
|
||||
|
||||
if(result.size() == 1 && result.iterator().next().endsWith(".")) {
|
||||
completion.complete(result.iterator().next().substring(token.length()), false);
|
||||
completion.complete(result.iterator().next().substring(lastToken.length()), false);
|
||||
}else {
|
||||
CompletionUtils.complete(completion, result);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user