content: package com.taobao.arthas.core.command.monitor200;
  
  import com.alibaba.arthas.deps.org.slf4j.Logger;
  import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
  import com.taobao.arthas.core.advisor.Advice;
  import com.taobao.arthas.core.advisor.AdviceListenerAdapter;
  import com.taobao.arthas.core.advisor.ArthasMethod;
  import com.taobao.arthas.core.shell.command.CommandProcess;
  import com.taobao.arthas.core.util.LogUtil;
  import com.taobao.arthas.core.util.ThreadLocalWatch;
  
  import java.util.concurrent.atomic.AtomicBoolean;
  
  /**
   * @author ralf0131 2017-01-06 16:02.
   */
  public class AbstractTraceAdviceListener extends AdviceListenerAdapter {
      private static final Logger logger = LoggerFactory.getLogger(AbstractTraceAdviceListener.class);
      protected final ThreadLocalWatch threadLocalWatch = new ThreadLocalWatch();
      protected TraceCommand command;
      protected CommandProcess process;
      private final AtomicBoolean processAborted = new AtomicBoolean(false);
  
      protected final ThreadLocal<TraceEntity> threadBoundEntity = new ThreadLocal<TraceEntity>();
  
      /**
       * Constructor
       */
      public AbstractTraceAdviceListener(TraceCommand command, CommandProcess process) {
          this.command = command;
          this.process = process;
      }
  
      protected TraceEntity threadLocalTraceEntity(ClassLoader loader) {
          TraceEntity traceEntity = threadBoundEntity.get();
          if (traceEntity == null) {
              traceEntity = new TraceEntity(loader);
              threadBoundEntity.set(traceEntity);
          }
          return traceEntity;
      }
  
      @Override
      public void destroy() {
          threadBoundEntity.remove();
      }
  
      @Override
      public void before(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target, Object[] args)
              throws Throwable {
          TraceEntity traceEntity = threadLocalTraceEntity(loader);
          traceEntity.tree.begin(clazz.getName(), method.getName(), -1, false);
          traceEntity.deep++;
          // 开始计算本次方法调用耗时
          threadLocalWatch.start();
      }
  
      @Override
      public void afterReturning(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target, Object[] args,
                                 Object returnObject) throws Throwable {
          threadLocalTraceEntity(loader).tree.end();
          final Advice advice = Advice.newForAfterReturning(loader, clazz, method, target, args, returnObject);
          finishing(loader, advice);
      }
  
      @Override
      public void afterThrowing(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target, Object[] args,
                                Throwable throwable) throws Throwable {
          int lineNumber = -1;
          StackTraceElement[] stackTrace = throwable.getStackTrace();
          if (stackTrace.length != 0) {
              lineNumber = stackTrace[0].getLineNumber();
          }
  
          threadLocalTraceEntity(loader).tree.end(throwable, lineNumber);
          final Advice advice = Advice.newForAfterThrowing(loader, clazz, method, target, args, throwable);
          finishing(loader, advice);
      }
  
      public TraceCommand getCommand() {
          return command;
      }
  
      private void finishing(ClassLoader loader, Advice advice) {
          // 本次调用的耗时
          TraceEntity traceEntity = threadLocalTraceEntity(loader);
          if (traceEntity.deep >= 1) { // #1817 防止deep为负数
              traceEntity.deep--;
          }
          if (traceEntity.deep == 0) {
              double cost = threadLocalWatch.costInMillis();
              try {
                  boolean conditionResult = isConditionMet(command.getConditionExpress(), advice, cost);
                  if (this.isVerbose()) {
                      process.write("Condition express: " + command.getConditionExpress() + " , result: " + conditionResult + "\n");
                  }
                  if (conditionResult) {
                      // 满足输出条件
                      process.times().incrementAndGet();
                      // TODO: concurrency issues for process.write
                      process.appendResult(traceEntity.getModel());
  
                      // 是否到达数量限制
                      if (isLimitExceeded(command.getNumberOfLimit(), process.times().get())) {
                          abortProcess(process, command.getNumberOfLimit());
                      }
                  }
              } catch (Throwable e) {
                  logger.warn("trace failed.", e);
                  process.end(1, "trace failed, condition is: " + command.getConditionExpress() + ", " + e.getMessage()
                                + ", visit " + LogUtil.loggingFile() + " for more details.");
              } finally {
                  threadBoundEntity.remove();
              }
          }
      }
  
      @Override
      protected void abortProcess(CommandProcess process, int limit) {
          // Only proceed if this thread is the first one to set the flag to true
          if (processAborted.compareAndSet(false, true)) {
              super.abortProcess(process, limit);
          }
      }
  }
  