Skip to content

Commit 0eff763

Browse files
authored
Merge pull request #28 from fabio-franco/merging_dgi_code_analyzer_with_minerva_java_analyzer
Merging dgi code analyzer with minerva java analyzer and improvements done for sdg generation
2 parents 6dd6c5d + 67c3838 commit 0eff763

15 files changed

Lines changed: 741 additions & 32 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target/

pom.xml

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,37 @@
2929
<dependency>
3030
<groupId>com.ibm.wala</groupId>
3131
<artifactId>com.ibm.wala.core</artifactId>
32-
<version>1.5.9</version>
32+
<version>1.5.10</version>
3333
</dependency>
3434
<dependency>
3535
<groupId>com.ibm.wala</groupId>
3636
<artifactId>com.ibm.wala.core.java11</artifactId>
37-
<version>1.5.9</version>
37+
<version>1.5.10</version>
3838
</dependency>
3939
<dependency>
4040
<groupId>com.ibm.wala</groupId>
4141
<artifactId>com.ibm.wala.cast</artifactId>
42-
<version>1.5.9</version>
42+
<version>1.5.10</version>
4343
</dependency>
4444
<dependency>
4545
<groupId>com.ibm.wala</groupId>
4646
<artifactId>com.ibm.wala.cast.java</artifactId>
47-
<version>1.5.9</version>
47+
<version>1.5.10</version>
4848
</dependency>
4949
<dependency>
5050
<groupId>com.ibm.wala</groupId>
5151
<artifactId>com.ibm.wala.cast.java.ecj</artifactId>
52-
<version>1.5.9</version>
52+
<version>1.5.10</version>
53+
</dependency>
54+
<dependency>
55+
<groupId>com.ibm.wala</groupId>
56+
<artifactId>com.ibm.wala.util</artifactId>
57+
<version>1.5.10</version>
58+
</dependency>
59+
<dependency>
60+
<groupId>com.ibm.wala</groupId>
61+
<artifactId>com.ibm.wala.shrike</artifactId>
62+
<version>1.5.10</version>
5363
</dependency>
5464
<dependency>
5565
<groupId>org.jgrapht</groupId>

src/main/java/com/ibm/minerva/analyzer/Analyzer.java

Lines changed: 79 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Arrays;
2727
import java.util.LinkedHashSet;
2828
import java.util.List;
29+
import java.util.Locale;
2930
import java.util.Optional;
3031
import java.util.Set;
3132
import java.util.StringTokenizer;
@@ -37,6 +38,7 @@ public class Analyzer {
3738
private static final Logger logger = LoggingUtil.getLogger(Analyzer.class);
3839

3940
private final File[] archives;
41+
private final List<File> additionalLibraries;
4042
private final File outputDir;
4143
private final TableBuilderConfiguration config;
4244
private final ApplicationProcessor ap;
@@ -59,13 +61,27 @@ public Analyzer(File[] archives, File outputDir) {
5961
}
6062

6163
public Analyzer(File[] archives, File outputDir, TableBuilderConfiguration config) {
64+
this(archives, outputDir, config, null);
65+
}
66+
67+
public Analyzer(File[] archives, File outputDir, File[] additionalLibs) {
68+
this(archives, outputDir, TableBuilderConfiguration.ALL, additionalLibs);
69+
}
70+
71+
public Analyzer(File[] archives, File outputDir, TableBuilderConfiguration config, File[] additionalLibs) {
6272
if (archives == null || Arrays.stream(archives).anyMatch(x -> x == null) || outputDir == null) {
6373
throw new NullPointerException();
6474
}
6575
this.archives = archives;
6676
this.outputDir = outputDir;
6777
this.config = (config != null) ? config : TableBuilderConfiguration.NONE;
6878
this.ap = new TableBuilder(outputDir, config);
79+
this.additionalLibraries = new ArrayList<File>();
80+
81+
if (additionalLibs != null) {
82+
addExtraLibraries(additionalLibs);
83+
}
84+
6985
}
7086

7187
public Analyzer setPackageRestrictions(Set<String> packages, boolean isPackageIncludeList) {
@@ -95,6 +111,37 @@ public Analyzer setCallGraphBuilder(CallGraphBuilderType type) throws IOExceptio
95111
ap.setCallGraphBuilder(type != null ? new CallGraphBuilder(type) : null);
96112
return this;
97113
}
114+
115+
public void addExtraLibrary(File extraLib) {
116+
if (extraLib != null) {
117+
final String name = extraLib.getName().toLowerCase(Locale.ENGLISH);
118+
final BinaryType bt = BinaryType.getBinaryType(name);
119+
if (bt == BinaryType.JAR) {
120+
this.additionalLibraries.add(extraLib);
121+
}
122+
else {
123+
logger.finest(() -> formatMessage("CallGraphFileIsNotJAR", extraLib.getAbsolutePath()));
124+
}
125+
}
126+
}
127+
128+
public void addExtraLibraries (File[] extraLibs) {
129+
// Add extra user provided JARS to scope (i.e., JEE libraries)
130+
if (extraLibs != null) {
131+
for (File extraLib : extraLibs) {
132+
// if is a directory, try to add any JAR inside the path as an extra library
133+
if (extraLib.isDirectory()) {
134+
File[] listOfExtraLibs = extraLib.listFiles();
135+
for (File e : listOfExtraLibs) {
136+
addExtraLibrary(e);
137+
}
138+
}
139+
else {
140+
addExtraLibrary(extraLib);
141+
}
142+
}
143+
}
144+
}
98145

99146
public void run() throws IOException {
100147
try {
@@ -118,6 +165,11 @@ public void run() throws IOException {
118165
logger.info(() -> formatMessage("AnalyzingArchive", archive));
119166
archiveProcessor.processBinaryFile(archive);
120167
}
168+
169+
// Add extra libraries, if is there any
170+
if (additionalLibraries != null && additionalLibraries.size() > 0) {
171+
archiveProcessor.processExtraLibs(additionalLibraries.toArray(new File[additionalLibraries.size()]));
172+
}
121173
}
122174
ap.write();
123175
}
@@ -133,45 +185,54 @@ public static void setLoggingLevel(Level level) {
133185

134186
// [0] : archive path(s)
135187
// [1] : output directory
136-
// [2] : package exclusion list
137-
// [3] : build call graph (true|false|<algorithm-name>)
188+
// [2] : additional libraries (especially JEE libraries)
189+
// [3] : package exclusion list
190+
// [4] : build call graph (true|false|<algorithm-name>)
138191
public static void main(String[] args) {
139192
if (args.length > 1) {
140193
final Analyzer analyzer;
141-
if (!args[0].contains(File.pathSeparator)) {
142-
analyzer = new Analyzer(new File(args[0]), new File(args[1]));
194+
final List<File> archives = new ArrayList<>();
195+
final List<File> extraLibs = new ArrayList<>();
196+
197+
final StringTokenizer st = new StringTokenizer(args[0], File.pathSeparator);
198+
while (st.hasMoreTokens()) {
199+
final String file = st.nextToken().trim();
200+
if (!file.isEmpty()) {
201+
archives.add(new File(file));
202+
}
143203
}
144-
else {
145-
final List<File> archives = new ArrayList<>();
146-
final StringTokenizer st = new StringTokenizer(args[0], File.pathSeparator);
147-
while (st.hasMoreTokens()) {
148-
final String file = st.nextToken().trim();
204+
if (args.length > 2) {
205+
final StringTokenizer st2 = new StringTokenizer(args[2], File.pathSeparator);
206+
while (st2.hasMoreTokens()) {
207+
final String file = st2.nextToken().trim();
149208
if (!file.isEmpty()) {
150-
archives.add(new File(file));
209+
extraLibs.add(new File(file));
151210
}
152211
}
153-
analyzer = new Analyzer(archives.toArray(new File[archives.size()]), new File(args[1]));
154212
}
155-
if (args.length > 2) {
213+
214+
analyzer = new Analyzer(archives.toArray(new File[archives.size()]), new File(args[1]), extraLibs.toArray(new File[extraLibs.size()]));
215+
216+
if (args.length > 3) {
156217
final Set<String> packages = new LinkedHashSet<>();
157-
final StringTokenizer st = new StringTokenizer(args[2], ",");
158-
while (st.hasMoreTokens()) {
159-
final String token = st.nextToken().trim();
218+
final StringTokenizer st3 = new StringTokenizer(args[3], ",");
219+
while (st3.hasMoreTokens()) {
220+
final String token = st3.nextToken().trim();
160221
if (!token.isEmpty()) {
161222
packages.add(token);
162223
}
163224
}
164225
analyzer.setPackageRestrictions(packages, false);
165226
}
166227
try {
167-
if (args.length > 3) {
168-
final boolean generateCallGraph = Boolean.parseBoolean(args[3]);
228+
if (args.length > 4) {
229+
final boolean generateCallGraph = Boolean.parseBoolean(args[4]);
169230
if (generateCallGraph) {
170231
analyzer.setCallGraphBuilder(generateCallGraph);
171232
}
172233
else {
173234
// Try to match the name of one of the CallGraphBuilderType enum values.
174-
Optional<CallGraphBuilderType> o = CallGraphBuilderType.find(args[3]);
235+
Optional<CallGraphBuilderType> o = CallGraphBuilderType.find(args[4]);
175236
if (o.isPresent()) {
176237
analyzer.setCallGraphBuilder(o.get());
177238
}

src/main/java/com/ibm/minerva/analyzer/ApplicationProcessor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818

1919
package com.ibm.minerva.analyzer;
2020

21+
import java.io.File;
2122
import java.io.IOException;
2223
import java.util.Set;
2324

2425
public interface ApplicationProcessor {
2526

2627
public void process(ClassProcessor cp, byte[] bytes);
28+
public void processExtraLibs(File[] extraLibs);
2729
public void setCallGraphBuilder(CallGraphBuilder cgb);
2830
public void setPackageRestrictions(Set<String> packages, boolean isPackageIncludeList);
2931
public void setAllowAnyLegalClasses(boolean allowAnyLegalClasses);

src/main/java/com/ibm/minerva/analyzer/ArchiveProcessor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public final class ArchiveProcessor {
5050
public ArchiveProcessor(ApplicationProcessor ap) {
5151
this.ap = ap;
5252
}
53+
54+
public void processExtraLibs(File[] extraLibs) {
55+
ap.processExtraLibs(extraLibs);
56+
}
5357

5458
public void processBinaryFile(File thisBinaryFile) throws IOException {
5559
if (!thisBinaryFile.exists()) {

src/main/java/com/ibm/minerva/analyzer/CallGraphBuilder.java

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.Iterator;
3535
import java.util.LinkedHashMap;
3636
import java.util.LinkedHashSet;
37+
import java.util.Locale;
3738
import java.util.Map;
3839
import java.util.Set;
3940
import java.util.jar.JarFile;
@@ -46,6 +47,7 @@
4647
import org.jgrapht.nio.json.JSONExporter;
4748

4849
import com.google.gson.JsonObject;
50+
import com.ibm.minerva.dgi.utils.SDGGraph2JSON;
4951
import com.ibm.wala.cast.ir.ssa.AstIRFactory;
5052
import com.ibm.wala.cast.java.ipa.callgraph.JavaSourceAnalysisScope;
5153
import com.ibm.wala.cast.java.translator.jdt.ecj.ECJClassLoaderFactory;
@@ -54,6 +56,7 @@
5456
import com.ibm.wala.classLoader.IMethod;
5557
import com.ibm.wala.classLoader.Module;
5658
import com.ibm.wala.classLoader.ModuleEntry;
59+
import com.ibm.wala.classLoader.PhantomClass;
5760
import com.ibm.wala.core.util.strings.Atom;
5861
import com.ibm.wala.ipa.callgraph.AnalysisCacheImpl;
5962
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
@@ -63,8 +66,13 @@
6366
import com.ibm.wala.ipa.callgraph.Entrypoint;
6467
import com.ibm.wala.ipa.callgraph.IAnalysisCacheView;
6568
import com.ibm.wala.ipa.callgraph.impl.DefaultEntrypoint;
69+
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
70+
import com.ibm.wala.ipa.cfg.InterproceduralCFG;
6671
import com.ibm.wala.ipa.cha.ClassHierarchyFactory;
6772
import com.ibm.wala.ipa.cha.IClassHierarchy;
73+
import com.ibm.wala.ipa.modref.ModRef;
74+
import com.ibm.wala.ipa.slicer.SDG;
75+
import com.ibm.wala.ipa.slicer.Slicer;
6876
import com.ibm.wala.properties.WalaProperties;
6977
import com.ibm.wala.ssa.SymbolTable;
7078
import com.ibm.wala.types.ClassLoaderReference;
@@ -214,16 +222,17 @@ public CallGraphBuilder(CallGraphBuilderType type) throws IOException {
214222
scope = createScope();
215223
}
216224

217-
public boolean write(File savePath) throws IOException {
225+
public boolean write(File callGraphFile, File sdgGraphFile) throws IOException {
218226
// Make class hierarchy
219227
try {
220228
if (classes.size() > 0) {
221229
// Create class hierarchy
222230
logger.info(() -> formatMessage("CallGraphClassHierarchyBuild"));
223-
IClassHierarchy cha = ClassHierarchyFactory.make(scope, new ECJClassLoaderFactory(scope.getExclusions()));
231+
IClassHierarchy cha = ClassHierarchyFactory.makeWithPhantom(scope, new ECJClassLoaderFactory(scope.getExclusions()));
224232

225233
logger.info(() -> formatMessage("CallGraphEndpointCalculation"));
226234
Collection<Entrypoint> entryPoints = getEntryPoints(cha);
235+
227236
if (entryPoints.size() > 0) {
228237
// Initialize analysis options
229238
AnalysisOptions options = new AnalysisOptions();
@@ -238,7 +247,37 @@ public boolean write(File savePath) throws IOException {
238247
CallGraph callGraph = builder.makeCallGraph(options, null);
239248

240249
// Save the call graph as JSON
241-
callgraph2JSON(callGraph, savePath);
250+
callgraph2JSON(callGraph, callGraphFile);
251+
252+
// Build System Dependency Graph (call graph with method information)
253+
logger.info(() -> formatMessage("CallGraphBuildMethodLevel"));
254+
255+
try {
256+
SDG<? extends InstanceKey> sdg = new SDG<>(
257+
callGraph,
258+
builder.getPointerAnalysis(),
259+
new ModRef<>(),
260+
Slicer.DataDependenceOptions.NO_HEAP_NO_EXCEPTIONS,
261+
Slicer.ControlDependenceOptions.NO_EXCEPTIONAL_EDGES);
262+
263+
// Build IPCFG
264+
InterproceduralCFG ipcfg_full = new InterproceduralCFG(callGraph,
265+
n -> n.getMethod().getReference().getDeclaringClass().getClassLoader() == JavaSourceAnalysisScope.SOURCE
266+
|| n == callGraph.getFakeRootNode() || n == callGraph.getFakeWorldClinitNode());
267+
268+
// Save System Dependency Graph as JSON (call graph with method information)
269+
SDGGraph2JSON.convertAndSave(sdg, callGraph, ipcfg_full, sdgGraphFile);
270+
271+
} catch (Throwable t) {
272+
if (t instanceof IOException) {
273+
throw (IOException) t;
274+
}
275+
logger.severe(() -> formatMessage("CallGraphWriteError", sdgGraphFile, t.getMessage()));
276+
throw new IOException(t);
277+
}
278+
279+
logger.info(() -> formatMessage("WritingFile", sdgGraphFile.getAbsolutePath()));
280+
242281
return true;
243282
}
244283
}
@@ -247,7 +286,7 @@ public boolean write(File savePath) throws IOException {
247286
if (t instanceof IOException) {
248287
throw (IOException) t;
249288
}
250-
logger.severe(() -> formatMessage("CallGraphWriteError", savePath, t.getMessage()));
289+
logger.severe(() -> formatMessage("CallGraphWriteError", callGraphFile, t.getMessage()));
251290
throw new IOException(t);
252291
}
253292
return false;
@@ -388,6 +427,25 @@ private File createTemporaryFile(String className, byte[] clazz) {
388427
}
389428
return null;
390429
}
430+
431+
public void addLibsToScope(File[] extraLibs) {
432+
for (File extraLibJar : extraLibs) {
433+
final String name = extraLibJar.getName().toLowerCase(Locale.ENGLISH);
434+
final BinaryType bt = BinaryType.getBinaryType(name);
435+
436+
if (bt == BinaryType.JAR) {
437+
logger.info(() -> formatMessage("CallGraphAddExtraLibToScope", extraLibJar.getName()));
438+
try {
439+
scope.addToScope(ClassLoaderReference.Extension, new JarFile(extraLibJar.getAbsolutePath()));
440+
} catch (Throwable t) {
441+
logger.severe(() -> formatMessage("CallGraphBuildError", t.getMessage()));
442+
}
443+
}
444+
else {
445+
logger.finest(() -> formatMessage("CallGraphFileIsNotJAR", extraLibJar.getAbsolutePath()));
446+
}
447+
}
448+
}
391449

392450
private AnalysisScope createScope() throws IOException {
393451
AnalysisScope scope = new JavaSourceAnalysisScope();
@@ -406,6 +464,7 @@ private AnalysisScope createScope() throws IOException {
406464
scope.addToScope(ClassLoaderReference.Primordial, new JarFile(stdlib));
407465
}
408466
}
467+
409468
// Add application module to scope.
410469
scope.addToScope(ClassLoaderReference.Application, module);
411470
}
@@ -433,7 +492,7 @@ private boolean isApplicationClass(IClass _class) {
433492
private Collection<Entrypoint> getEntryPoints(IClassHierarchy cha) {
434493
final Collection<Entrypoint> entrypoints = new ArrayList<>();
435494
cha.forEach(c -> {
436-
if (isApplicationClass(c)) {
495+
if (isApplicationClass(c) && !(c instanceof PhantomClass)) {
437496
c.getDeclaredMethods().forEach(method -> {
438497
entrypoints.add(new DefaultEntrypoint(method, cha));
439498
});

0 commit comments

Comments
 (0)