watch/monitor/trace/tt/stack support better auto completion. close #497

pull/500/head
hengyunabc 6 years ago
parent 9f57c2c9a6
commit 7b9b4107ad

@ -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
}
}

@ -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);
}

Loading…
Cancel
Save