java-callgraph

Details

diff --git a/src/main/java/gr/gousiosg/javacg/dyn/Graph.java b/src/main/java/gr/gousiosg/javacg/dyn/Graph.java
new file mode 100644
index 0000000..2e9bf05
--- /dev/null
+++ b/src/main/java/gr/gousiosg/javacg/dyn/Graph.java
@@ -0,0 +1,18 @@
+package gr.gousiosg.javacg.dyn;
+
+import java.util.Stack;
+
+public class Graph {
+
+    private static Stack<String> stack = new Stack<String>();
+
+    public static void pushNode(String className, String methodName) {
+        String entry = className + ":" + methodName;
+        System.err.println(stack.peek() + " " + entry);
+        stack.push(entry);
+    }
+
+    public static void popNode() {
+        stack.pop();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/gr/gousiosg/javacg/dyn/Instrumenter.java b/src/main/java/gr/gousiosg/javacg/dyn/Instrumenter.java
new file mode 100644
index 0000000..5fedb11
--- /dev/null
+++ b/src/main/java/gr/gousiosg/javacg/dyn/Instrumenter.java
@@ -0,0 +1,126 @@
+package gr.gousiosg.javacg.dyn;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.Instrumentation;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.NotFoundException;
+
+public class Instrumenter implements ClassFileTransformer {
+
+    static List<Pattern> pkgIncl = new ArrayList<Pattern>();
+    static List<Pattern> pkgExcl = new ArrayList<Pattern>();
+
+    public static void premain(String argument,
+            Instrumentation instrumentation) {
+        
+        //incl=com.foo.*,gr.bar.foo;excl=com.bar.foo.*
+        
+        String[] tokens = argument.split(";");
+        
+        if (tokens.length <= 1) {
+            System.err.print("Missing delimeter ; from argument:" + tokens);
+            return;
+        }
+        
+        for (String token : tokens) {
+            String[] args = token.split("="); 
+            if (args.length <= 2) {
+                System.err.print("Missing argument delimeter =:" + token);
+                return;
+            }
+            
+            String argtype = args[0];
+            
+            if (!argtype.equals("inc") || !argtype.equals("excl")) {
+                System.err.print("wrong argument:" + argtype);
+                return;
+            }
+            
+            String[] patterns = args[1].split(",");
+            
+            for (String pattern : patterns) {
+                Pattern p = null;
+                try {
+                    p = Pattern.compile(pattern);
+                } catch (PatternSyntaxException pse) {
+                    System.err.print("pattern: " + pattern + " not valid, ignoring");
+                }
+                if (argtype.equals("inc"))
+                    pkgIncl.add(p);
+                else 
+                    pkgExcl.add(p);
+            }
+        }
+        
+        instrumentation.addTransformer(new Instrumenter());
+    }
+
+    public byte[] transform(ClassLoader loader, String className, Class clazz,
+            java.security.ProtectionDomain domain, byte[] bytes) {
+        boolean enhanceClass = false;
+        
+        for (Pattern p : pkgIncl) {
+            Matcher m = p.matcher(clazz.getCanonicalName());
+            if (m.matches()) {
+                enhanceClass = true;
+                break;
+            }
+        }
+        
+        for (Pattern p : pkgExcl) {
+            Matcher m = p.matcher(clazz.getCanonicalName());
+            if (m.matches()) {
+                enhanceClass = false;
+                break;
+            }
+        }
+
+        if (enhanceClass) {
+            return enhanceClass(className, bytes);
+        } else {
+            return bytes;
+        }
+    }
+
+    private byte[] enhanceClass(String name, byte[] b) {
+        ClassPool pool = ClassPool.getDefault();
+        CtClass clazz = null;
+        try {
+            clazz = pool.makeClass(new java.io.ByteArrayInputStream(b));
+            if (!clazz.isInterface()) {
+                CtBehavior[] methods = clazz.getDeclaredBehaviors();
+                for (int i = 0; i < methods.length; i++) {
+                    if (!methods[i].isEmpty()) {
+                        enhanceMethod(methods[i], clazz.getName());
+                    }
+                }
+                b = clazz.toBytecode();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException("Could not instrument  " + name
+                    + ",  exception : " + e.getMessage());
+        } finally {
+            if (clazz != null) {
+                clazz.detach();
+            }
+        }
+        return b;
+    }
+
+    private void enhanceMethod(CtBehavior method, String className)
+            throws NotFoundException, CannotCompileException {
+        method.insertBefore("gr.gousiosg.javacg.dyn.push(\"" + className
+                + "\",\"" + method.getName() + "\");");
+        method.insertAfter("gr.gousiosg.javacg.dyn.pop();");
+    }
+}
\ No newline at end of file