package com.github.ompc.greys.core.command; import com.github.ompc.greys.core.Advice; import com.github.ompc.greys.core.advisor.AdviceListener; import com.github.ompc.greys.core.advisor.ReflectAdviceListenerAdapter; import com.github.ompc.greys.core.command.annotation.Cmd; import com.github.ompc.greys.core.command.annotation.IndexArg; import com.github.ompc.greys.core.command.annotation.NamedArg; import com.github.ompc.greys.core.exception.ExpressException; import com.github.ompc.greys.core.server.Session; import com.github.ompc.greys.core.util.InvokeCost; import com.github.ompc.greys.core.util.PointCut; import com.github.ompc.greys.core.util.matcher.ClassMatcher; import com.github.ompc.greys.core.util.matcher.GaMethodMatcher; import com.github.ompc.greys.core.util.matcher.PatternMatcher; import java.lang.instrument.Instrumentation; import java.util.concurrent.atomic.AtomicInteger; import static com.github.ompc.greys.core.util.Express.ExpressFactory.newExpress; import static com.github.ompc.greys.core.util.GaStringUtils.getStack; import static com.github.ompc.greys.core.util.GaStringUtils.getThreadInfo; import static org.apache.commons.lang3.StringUtils.isBlank; /** * Jstack命令<br/> * 负责输出当前方法执行上下文 * * @author oldmanpushcart@gmail.com */ @Cmd(name = "stack", sort = 6, summary = "Display the stack trace of specified class and method", eg = { "stack -E org\\.apache\\.commons\\.lang\\.StringUtils isBlank", "stack org.apache.commons.lang.StringUtils isBlank", "stack *StringUtils isBlank", "stack *StringUtils isBlank 'params[0].length==1'", "stack *StringUtils isBlank '#cost>100'" }) public class StackCommand implements Command { @IndexArg(index = 0, name = "class-pattern", summary = "Path and classname of Pattern Matching") private String classPattern; @IndexArg(index = 1, name = "method-pattern", isRequired = false, summary = "Method of Pattern Matching") private String methodPattern; @IndexArg(index = 2, name = "condition-express", isRequired = false, summary = "Conditional expression by OGNL", description = "" + "FOR EXAMPLE" + "\n" + " TRUE : 1==1\n" + " TRUE : true\n" + " FALSE : false\n" + " TRUE : params.length>=0\n" + " FALSE : 1==2\n" + "\n" + "THE STRUCTURE" + "\n" + " target : the object \n" + " clazz : the object's class\n" + " method : the constructor or method\n" + " params[0..n] : the parameters of method\n" + " returnObj : the returned object of method\n" + " throwExp : the throw exception of method\n" + " isReturn : the method ended by return\n" + " isThrow : the method ended by throwing exception" ) private String conditionExpress; @NamedArg(name = "E", summary = "Enable regular expression to match (wildcard matching by default)") private boolean isRegEx = false; @NamedArg(name = "n", hasValue = true, summary = "Threshold of execution times") private Integer threshold; @Override public Action getAction() { return new GetEnhancerAction() { @Override public GetEnhancer action(Session session, Instrumentation inst, final Printer printer) throws Throwable { return new GetEnhancer() { private final AtomicInteger times = new AtomicInteger(); @Override public PointCut getPointCut() { return new PointCut( new ClassMatcher(new PatternMatcher(isRegEx, classPattern)), new GaMethodMatcher(new PatternMatcher(isRegEx, methodPattern)) ); } @Override public AdviceListener getAdviceListener() { return new ReflectAdviceListenerAdapter() { private final ThreadLocal<String> stackInfoRef = new ThreadLocal<String>(); private final InvokeCost invokeCost = new InvokeCost(); private String getTitle(final Advice advice) { final StringBuilder titleSB = new StringBuilder(getThreadInfo()); if (advice.isTraceSupport()) { titleSB.append(";traceId=").append(advice.getTraceId()).append(";"); } return titleSB.toString(); } @Override public void before(Advice advice) throws Throwable { stackInfoRef.set(getStack(getTitle(advice))); invokeCost.begin(); } private boolean isInCondition(Advice advice, long cost) { try { return isBlank(conditionExpress) || newExpress(advice).bind("cost", cost).is(conditionExpress); } catch (ExpressException e) { return false; } } private boolean isOverThreshold(int currentTimes) { return null != threshold && currentTimes >= threshold; } @Override public void afterFinishing(Advice advice) throws Throwable { if (isInCondition(advice, invokeCost.cost())) { printer.println(stackInfoRef.get()); if (isOverThreshold(times.incrementAndGet())) { printer.finish(); } } } }; } }; } }; } }