如何从 Soot 生成的调用图中排除 java 个标准库?
How do I exclude java standard libraries from call graphs generated by Soot?
我目前正在开发自动代码文档工具。为此,我使用 Soot 来构建调用图。但是,Soot 似乎在此调用图中包含了标准 java 库。这当然是不可取的,因为我只对我将为其生成文档的程序的实际 类 感兴趣。
这是我用来测试调用图的程序:
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));
}
使用以下函数迭代调用图(取自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);
}
}
}
然而,在测试时我一直在记录这样的电话:
[...]
<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>()>
[...]
随后 java 个库之间进行了大量调用。
有没有办法让 Soot 忽略标准 java 库?
好的,我找到了解决方案。
我们只是抑制来自 java 包的 class 的 visit
调用。
所以使用 child.isJavaLibraryMethod()
检查它是否来自 java 包。
如果它来自 java 包,我们根本不会用 class 调用 visit
,因此通过添加对父子调用的检查并抑制输出,我们得到了正确的调用图。 (作为奖励,它更快,因为你不再遍历 java 库。
所以代码改为:
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);
}
}
}
我目前正在开发自动代码文档工具。为此,我使用 Soot 来构建调用图。但是,Soot 似乎在此调用图中包含了标准 java 库。这当然是不可取的,因为我只对我将为其生成文档的程序的实际 类 感兴趣。
这是我用来测试调用图的程序:
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));
}
使用以下函数迭代调用图(取自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);
}
}
}
然而,在测试时我一直在记录这样的电话:
[...]
<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>()>
[...]
随后 java 个库之间进行了大量调用。
有没有办法让 Soot 忽略标准 java 库?
好的,我找到了解决方案。
我们只是抑制来自 java 包的 class 的 visit
调用。
所以使用 child.isJavaLibraryMethod()
检查它是否来自 java 包。
如果它来自 java 包,我们根本不会用 class 调用 visit
,因此通过添加对父子调用的检查并抑制输出,我们得到了正确的调用图。 (作为奖励,它更快,因为你不再遍历 java 库。
所以代码改为:
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);
}
}
}