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?
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);
}
}
}