WALA之源代码生成调用关系图

步骤1 取下wala项目1.5.0源码,按照说明导入eclipse项目中(用gradle wrapper),执行后处理后,按照另一边博文说明修改build.gradle进行处理。然后可以编译通过。

步骤2 支持横向剪切调用关系图,就是保留自有方法调用自有方法,自有方法调用核心方法(jdk方法),去掉核心方法调用核心方法去掉。wala已经实现的剪切是按照lib库裁剪,比如将java/lang/*都排除掉,那么最后的调用关系图都不会出现这个包任何方法。显然不能满足我的要求。通过阅读源码,摸索出的方法如下:

/com.ibm.wala.cast.java.ecj/src/com/ibm/wala/cast/java/ecj/util/ExampleUtil.java

package com.ibm.wala.cast.java.ecj.util;

....

public class ExampleUtil {
	
	// more aggressive exclusions to avoid library blowup
	  // in interprocedural tests
private static final String EXCLUSIONS = "java\\/awt\\/.*\n" + 
		"javax\\/swing\\/.*\n" + 
		"sun\\/awt\\/.*\n" + 
		"sun\\/swing\\/.*\n" + 
		"com\\/sun\\/.*\n" + 
		"sun\\/.*\n" + 
		"org\\/netbeans\\/.*\n" + 
		"org\\/openide\\/.*\n" + 
		"com\\/ibm\\/crypto\\/.*\n" + 
		"com\\/ibm\\/security\\/.*\n" + 
		"org\\/apache\\/xerces\\/.*\n" + 
		"java\\/security\\/.*\n" +
			//"java\\/util\\/.*\n" +	//added by me
			//"java\\/io\\/.*\n" + //added by me
			//"java\\/lang\\/.*\n" + //added by me
		"";

private static final String TAILEXCLUSIONS = "java\\/awt\\/.*\n" +   //新增剪切包,列表里的类只会出现一次
		"javax\\/swing\\/.*\n" + 
		"sun\\/awt\\/.*\n" + 
		"sun\\/swing\\/.*\n" + 
		"com\\/sun\\/.*\n" + 
		"sun\\/.*\n" + 
		"org\\/netbeans\\/.*\n" + 
		"org\\/openide\\/.*\n" + 
		"com\\/ibm\\/crypto\\/.*\n" + 
		"com\\/ibm\\/security\\/.*\n" + 
		"org\\/apache\\/xerces\\/.*\n" + 
		"java\\/security\\/.*\n" +
		"java\\/util\\/.*\n" +	//added by me
		"java\\/io\\/.*\n" + //added by me
		"java\\/lang\\/.*\n" + //added by me
		"";

public static void addDefaultExclusions(AnalysisScope scope) throws UnsupportedEncodingException, IOException {
	    scope.setExclusions(new FileOfClasses(new ByteArrayInputStream(ExampleUtil.EXCLUSIONS.getBytes("UTF-8"))));
}
//增加剪切包补充到scope类内部,用于调用图的生成处理
public static void addTailExclusions(AnalysisScope scope) throws UnsupportedEncodingException, IOException {
    scope.setTailExclusions(new FileOfClasses(new ByteArrayInputStream(ExampleUtil.TAILEXCLUSIONS.getBytes("UTF-8"))));
}
}

/com.ibm.wala.cast.java.ecj/src/com/ibm/wala/cast/java/ecj/util/SourceDirCallGraph.java

  public static void main(String[] args) throws ClassHierarchyException, IllegalArgumentException, CallGraphBuilderCancelException, IOException, WalaException {
    long start = System.currentTimeMillis();
    Properties p = CommandLine.parse(args);
    String sourceDir = p.getProperty("sourceDir");
    String mainClass = p.getProperty("mainClass");
    
    //add 路径
    sourceDir = "I:\\ideaPjt\\WALA\\com.ibm.wala.cast.java.test.data\\src\\foo\\bar\\hello\\world";
    mainClass = "Lfoo/bar/hello/world/InnerClasses";
    
    AnalysisScope scope = new JavaSourceAnalysisScope();
    // add standard libraries to scope
    String[] stdlibs = WalaProperties.getJ2SEJarFiles();
    for (String stdlib : stdlibs) {
      scope.addToScope(ClassLoaderReference.Primordial, new JarFile(stdlib));
    }
    
    ExampleUtil.addDefaultExclusions(scope);	//增加无效lib库的裁减
    ExampleUtil.addTailExclusions(scope);	//增加无效类的剪切
    

/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/SSAPropagationCallGraphBuilder.java

 private void processResolvedCall(CGNode caller, SSAAbstractInvokeInstruction instruction, CGNode target,
      InstanceKey[][] constParams, PointerKey uniqueCatchKey) {

    if (DEBUG) {
      System.err.println("processResolvedCall: " + caller + " ," + instruction + " , " + target);
    }

    if (DEBUG) {
      System.err.println("addTarget: " + caller + " ," + instruction + " , " + target);
    }
    caller.addTarget(instruction.getCallSite(), target);

    if (callGraph.getFakeRootNode().equals(caller)) {
      if (entrypointCallSites.contains(instruction.getCallSite())) {
        callGraph.registerEntrypoint(target);
      }
    }

    if (!haveAlreadyVisited(target)) {
      //markDiscovered(target);  //找到了 删除
      System.out.println(caller.toString() + " caller.");
      System.out.println(target.toString() + " target.");
      //获取待剪切的模式串
      SetOfClasses setOfClasses = getClassHierarchy().getScope().getTailExclusions();      
      if (setOfClasses != null) { 
        //不空,就判断被调用者是否在剪切模式中
        boolean target_IsExcluded = setOfClasses.contains(target.toString().split(",")[1].trim().substring(1));
        //判断调用者是否在剪切模式中
        boolean caller_IsExcluded = setOfClasses.contains(caller.toString().split(",")[1].trim().substring(1));
        //int caller_index = caller.getMethod().getDeclaringClass().toString().indexOf("java/lang");
        // 父结点是排除类别,同时 子节点是排除类别,处理终止
        if(target_IsExcluded && caller_IsExcluded){
          System.out.println("discarded!!!");        
        }
        else {
          System.out.println("saved!!!"); 
          markDiscovered(target);
        }
      }
      else {
        //为空,直接添加被调用结点到调用图中
        markDiscovered(target);
      }
    }
    
    processCallingConstraints(caller, instruction, target, constParams, uniqueCatchKey);
  }

步骤3:生成dot文件和对应的pdf可视化图。原有的方法只是统计了图的信息,我需要的是dot文件做进一步处理,摸索的方法如下:

/com.ibm.wala.util/src/com/ibm/wala/viz/DotUtil.java

  public static <T> void dotify(Graph<T> g, NodeDecorator<T> labels, String title, String dotFile, String outputFile, String dotExe)
      throws WalaException {
    if (g == null) {
      throw new IllegalArgumentException("g is null");
    }
    File f = DotUtil.writeDotFile(g, labels, title, dotFile);
    if (dotExe != null && outputFile != null) { //如果输出pdf不为空,那么输出pdf文件
      spawnDot(dotExe, outputFile, f);
    }
  }

/com.ibm.wala.cast.java.ecj/src/com/ibm/wala/cast/java/ecj/util/SourceDirCallGraph.java

public class SourceDirCallGraph {

  /**
   * Usage: ScopeFileCallGraph -sourceDir file_path -mainClass class_name
   * 
   * If given -mainClass, uses main() method of class_name as entrypoint.  Class
   * name should start with an 'L'.
   * 
   * Example args: -sourceDir /tmp/srcTest -mainClass LFoo
   * 
   */
  //指定生成的文件,和读取的源文件,以及调用的系统命令	
  public static final String DOT_EXE = "I:/tool/graphviz-2.38/release/bin/dot";	
  public final static String DOT_FILE = "H:\\data\\dotfiles\\temp.dt";
  public final static String PDF_FILE = "H:\\data\\dotfiles\\temp.pdf";
	
  public static void main(String[] args) throws ClassHierarchyException, IllegalArgumentException, CallGraphBuilderCancelException, IOException, WalaException {
    long start = System.currentTimeMillis();
    Properties p = CommandLine.parse(args);
    String sourceDir = p.getProperty("sourceDir");
    String mainClass = p.getProperty("mainClass");
    
    //add 路径
    sourceDir = "I:\\ideaPjt\\WALA\\com.ibm.wala.cast.java.test.data\\src\\foo\\bar\\hello\\world";
    mainClass = "Lfoo/bar/hello/world/InnerClasses";
    
    AnalysisScope scope = new JavaSourceAnalysisScope();
    // add standard libraries to scope
    String[] stdlibs = WalaProperties.getJ2SEJarFiles();
    for (String stdlib : stdlibs) {
      scope.addToScope(ClassLoaderReference.Primordial, new JarFile(stdlib));
    }
    
    ExampleUtil.addDefaultExclusions(scope);	//增加无效lib库的裁减
    ExampleUtil.addTailExclusions(scope);	//增加无效类的剪切
    
    // add the source directory

     ...

    System.out.println("building call graph...");
    CallGraph cg = builder.makeCallGraph(options, null);
    //System.out.println(cg.toString());
    long end = System.currentTimeMillis();
    System.out.println("done");
    System.out.println("took " + (end-start) + "ms");
    System.out.println(CallGraphStats.getStats(cg));    
     
    DotUtil.dotify(cg, null, DOT_FILE, PDF_FILE, DOT_EXE);	//生成dot+pdf文件
    //DotUtil.dotify(cg, null, DOT_FILE, null, DOT_EXE);	//单生成dot文件
  }

猜你喜欢

转载自blog.csdn.net/xiexie1357/article/details/88726572