步骤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文件
}