code: /**
   * Copyright (C) . - All Rights Reserved.
   * Unauthorized copying of this file via any medium is
   * strictly prohibited Proprietary and Confidential.
   * Written by .
   */
  
  package pda.core.trace;
  
  import pda.common.conf.Constant;
  import pda.common.java.Subject;
  import pda.common.utils.Identifier;
  import pda.common.utils.LevelLogger;
  import pda.common.utils.Method;
  import pda.common.utils.Utils;
  import pda.core.trace.inst.Instrument;
  import pda.core.trace.inst.visitor.StatementInstrumentVisitor;
  import pda.core.trace.inst.visitor.TraceTestMethodInstrumentVisitor;
  import pda.core.trace.inst.visitor.TraversalVisitor;
  
  import java.io.File;
  import java.util.HashSet;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Set;
  
  /**
   * @author:
   * @date: 2021/11/16
   */
  public class Tracer {
  
      private Subject _subject;
      private Set<Integer> _testcases;
      private Set<Integer> _instMethods;
  
      public Tracer(Subject subject) {
          _subject = subject;
      }
  
      public void setTestCases(final Set<Integer> testcases) {
          _testcases = testcases;
      }
  
      public void setInstrumentMethods(final Set<Integer> methods) {
          _instMethods = methods;
      }
  
      private List<File> buildFiles(String base, Set<Integer> ids) {
          List<File> files = new LinkedList<>();
          Set<String> paths = new HashSet<>();
          for (Integer method : ids) {
              Method m = Method.parse(Identifier.getMessage(method));
              if (m == null) {
                  LevelLogger.error("Parse method id error : " + method);
                  continue;
              }
              String clazz = m.getClazz();
              if (clazz.contains("$")) {
                  clazz = clazz.substring(0, clazz.indexOf("$"));
              }
              String f = Utils.join(base, clazz.replace(".", Constant.DIR_SEPARATOR) + ".java");
              if (paths.contains(f)) {
                  continue;
              }
              paths.add(f);
              File file = new File(f);
              if (file.exists()) {
                  files.add(file);
              } else {
                  LevelLogger.error("File not exist : " + file.getAbsolutePath());
              }
          }
          return files;
      }
  
      public void trace(String outPath) {
          // _subject.restoreSource();
  
          List<File> srcFiles = buildFiles(Utils.join(_subject.getHome(), _subject.getSsrc()), _instMethods);
          List<File> testFiles = buildFiles(Utils.join(_subject.getHome(), _subject.getTsrc()), _testcases);
  
          LevelLogger.debug("Start to instrument source code...");
          TraversalVisitor traversalVisitor = new StatementInstrumentVisitor(_instMethods);
          Instrument.execute(traversalVisitor, srcFiles);
          LevelLogger.debug("Finish to instrument.");
  
          LevelLogger.debug("Start to instrument test code...");
          TraceTestMethodInstrumentVisitor newTestMethodInstrumentVisitor = new TraceTestMethodInstrumentVisitor(
                  _testcases);
          Instrument.execute(newTestMethodInstrumentVisitor, testFiles);
          LevelLogger.debug("Start to instrument.");
  
          // delete all bin file to make it re-compiled
          _subject.clearClass();
          Utils.deleteFiles(Constant.STR_TMP_TRACE_OUTPUT_FILE);
  
          for (Integer methodID : _testcases) {
              Method testcase = Method.parse(Identifier.getMessage(methodID));
              if (testcase == null)
                  continue;
              String singleTest = testcase.getClazz() + "::" + testcase.getName();
              boolean success = Runner.testSingleCase(_subject, singleTest);
              if (!success) {
                  LevelLogger.error("#collectCoveredMethod build subject failed when running single test case.");
                  System.exit(0);
              }
          }
  
          File file = new File(Constant.STR_TMP_TRACE_OUTPUT_FILE);
          String target = Utils.join(outPath, file.getName());
          Utils.copyFile(file, new File(target));
          LevelLogger.debug("Save trace file to : " + target);
          _subject.restoreSource();
      }
  
  }
  