Search code examples
javastatic-analysiscall-graphsoot

How do I exclude java standard libraries from call graphs generated by Soot?


I currently working on an automatic code documentation tool. For it, I am using Soot for constructing the call graph. However, Soot seems to be including the standard java libraries in this call graph. This is of course, not desirable since I am only interested in the actual classes of the program that I will be generating documentation for.

This is program I used to test the callgraph:

    public static void main(String[] args) throws FileNotFoundException {

        List<String> argsList = new ArrayList<String>(Arrays.asList(new String[0]));
        argsList.addAll(Arrays.asList(new String[]{
            "-w",
            "-no-bodies-for-excluded",
            "-process-dir",
            args[1], //directory with the java files
            "-src-prec",
            "java",
            "-main-class",
            args[2] //main class
        }));

        String[] trueArgs = argsList.toArray(new String[0]);
        Main.v().run(trueArgs);
        CallGraph cg = Scene.v().getCallGraph();
        visit(cg , Scene.v().getEntryPoints().get(0));
    }

With following function to iterate over the call graph (taken from this question):

    private static void visit(CallGraph cg, SootMethod method) {
          String identifier = method.getSignature();
          visited.put(method.getSignature(), true);
          dot.drawNode(identifier);
          // iterate over unvisited parents
          Iterator<MethodOrMethodContext> ptargets = new Sources(cg.edgesInto(method));
          if (ptargets != null) {
            while (ptargets.hasNext()) {
                SootMethod parent = (SootMethod) ptargets.next();
                if (!visited.containsKey(parent.getSignature())) visit(cg, parent);
            }
          }
          // iterate over unvisited children
          Iterator<MethodOrMethodContext> ctargets = new Targets(cg.edgesOutOf(method));
          if (ctargets != null) {
            while (ctargets.hasNext()) {
               SootMethod child = (SootMethod) ctargets.next();
               dot.drawEdge(identifier, child.getSignature());
               System.out.println(method + " may call " + child);
               if (!visited.containsKey(child.getSignature())) visit(cg, child);
            }
          }
    }

However, while testing I have been recording such calls:

[...]
<callgraphs.A: void <init>()> may call <java.lang.Object: void <init>()>
<java.lang.Thread: void <init>(java.lang.ThreadGroup,java.lang.Runnable)> may call <java.lang.Object: void <clinit>()>
<java.lang.Thread: void <init>(java.lang.ThreadGroup,java.lang.String)> may call <java.lang.Object: void <clinit>()>
<java.lang.Thread: void <init>(java.lang.ThreadGroup,java.lang.String)> may call <java.lang.Object: void <init>()>
<java.lang.Thread: void <init>(java.lang.ThreadGroup,java.lang.String)> may call <java.lang.Thread: void init(java.lang.ThreadGroup,java.lang.Runnable,java.lang.String,long)>
<java.lang.Thread: void <init>(java.lang.ThreadGroup,java.lang.String)> may call <java.lang.Object: void <init>()>
[...]

Followed by lots of calls between java libraries.

Is there a way to make Soot simply ignore the standard java libraries?


Solution

  • Ok, I found a solution.

    We just suppress the visit calls from classes from the java package.

    so by using child.isJavaLibraryMethod() to check if it is from the java package.

    If it is from the java package we simply do not call visit with that class, so by adding this check for the parent and child calls and suppressing the output, we get the correct callgraph. (and as a bonus it is much faster as you're not traversing the java library anymore.

    so the code is changed to:

    private static void visit(CallGraph cg, SootMethod method) {
              String identifier = method.getSignature();
              visited.put(method.getSignature(), true);
              dot.drawNode(identifier);
              // iterate over unvisited parents
              Iterator<MethodOrMethodContext> ptargets = new Sources(cg.edgesInto(method));
              if (ptargets != null) {
                while (ptargets.hasNext()) {
                    SootMethod parent = (SootMethod) ptargets.next();
                    if (!visited.containsKey(parent.getSignature()) && !parent.isJavaLibraryMethod()) visit(cg, parent);
                }
              }
              // iterate over unvisited children
              Iterator<MethodOrMethodContext> ctargets = new Targets(cg.edgesOutOf(method));
              if (ctargets != null) {
                while (ctargets.hasNext()) {
                   SootMethod child = (SootMethod) ctargets.next();
                   dot.drawEdge(identifier, child.getSignature());
                   if (!child.isJavaLibraryMethod())System.out.println(method + " may call " + child);
                   if (!visited.containsKey(child.getSignature()) && !child.isJavaLibraryMethod()) visit(cg, child);
                }
              }
        }