From 7b9b4107ad11261ee16a129be5fd0c541c9bf59b Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 8 Feb 2019 03:02:31 +0800 Subject: [PATCH] watch/monitor/trace/tt/stack support better auto completion. close #497 --- .../command/monitor200/CompleteContext.java | 53 ----- .../command/monitor200/EnhancerCommand.java | 200 ++---------------- .../command/monitor200/MonitorCommand.java | 7 - .../core/command/monitor200/StackCommand.java | 6 - .../command/monitor200/TimeTunnelCommand.java | 7 - .../core/command/monitor200/TraceCommand.java | 7 - .../core/command/monitor200/WatchCommand.java | 15 +- .../core/shell/cli/CompletionUtils.java | 17 +- 8 files changed, 46 insertions(+), 266 deletions(-) delete mode 100644 core/src/main/java/com/taobao/arthas/core/command/monitor200/CompleteContext.java diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/CompleteContext.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/CompleteContext.java deleted file mode 100644 index ed923e8c5..000000000 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/CompleteContext.java +++ /dev/null @@ -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 - } -} diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java index 49dbfbe5e..5c48a6094 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java @@ -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 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 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> results = SearchUtils.searchClassOnly(completion.session().getInstrumentation(), - "*" + lastToken.value() + "*", SIZE_LIMIT); - if (results.size() >= SIZE_LIMIT) { - Iterator> it = results.iterator(); - List res = new ArrayList(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 res = new ArrayList(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 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> 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 res = new ArrayList(); - - 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 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 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) { diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/MonitorCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/MonitorCommand.java index 1fbe7c49a..e779c6ff5 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/MonitorCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/MonitorCommand.java @@ -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; - } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java index 237806f7f..263c69b3c 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java @@ -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; - } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java index 4282fc76c..46216c6fa 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java @@ -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(); diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java index e08ca3a35..70849e1b8 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java @@ -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; - } - /** * 构造追踪路径匹配 */ diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java index e0d6de15b..8796260a8 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java @@ -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)); + } } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java b/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java index 365f55355..14737fa7a 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java @@ -156,9 +156,13 @@ public class CompletionUtils { public static boolean completeClassName(Completion completion) { List 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 result = new HashSet(); 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); }