From 7daf24ae7ff1e8c0ab968d1a505b89b9c48cdd76 Mon Sep 17 00:00:00 2001 From: David Krebs Date: Sun, 10 Dec 2017 18:13:13 +0100 Subject: [PATCH] several improvements --- .../com/dkarv/jdcallgraph/CallRecorder.java | 10 ++-- .../jdcallgraph/FieldAccessRecorder.java | 14 +++--- .../java/com/dkarv/jdcallgraph/Tracer.java | 46 +++++++++++++++++-- .../jdcallgraph/callgraph/CallGraph.java | 6 +-- .../com/dkarv/jdcallgraph/util/StackItem.java | 14 +++++- .../writer/CsvCoverageFileWriter.java | 16 ++++--- .../writer/CsvTraceFileWriter.java | 12 +++-- .../dkarv/jdcallgraph/EnhanceMethodTest.java | 2 +- 8 files changed, 92 insertions(+), 28 deletions(-) diff --git a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/CallRecorder.java b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/CallRecorder.java index 683ac7d..175730e 100644 --- a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/CallRecorder.java +++ b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/CallRecorder.java @@ -39,7 +39,8 @@ public class CallRecorder { */ static final Map GRAPHS = new HashMap<>(); - public static void beforeMethod(String className, String methodName, int lineNumber) { + public static void beforeMethod(String className, String methodName, int lineNumber, + boolean isTest) { try { LOG.trace("beforeMethod: {}:{}#{}", className, methodName, lineNumber); long threadId = Thread.currentThread().getId(); @@ -48,13 +49,14 @@ public static void beforeMethod(String className, String methodName, int lineNum graph = new CallGraph(threadId); GRAPHS.put(threadId, graph); } - graph.called(new StackItem(className, methodName, lineNumber)); + graph.called(new StackItem(className, methodName, lineNumber, isTest)); } catch (Throwable e) { LOG.error("Error in beforeMethod", e); } } - public static void afterMethod(String className, String methodName, int lineNumber) { + public static void afterMethod(String className, String methodName, int lineNumber, + boolean isTest) { try { LOG.trace("afterMethod: {}:{}#{}", className, methodName, lineNumber); long threadId = Thread.currentThread().getId(); @@ -63,7 +65,7 @@ public static void afterMethod(String className, String methodName, int lineNumb // not interesting return; } - graph.returned(new StackItem(className, methodName, lineNumber)); + graph.returned(new StackItem(className, methodName, lineNumber, isTest)); } catch (Throwable e) { LOG.error("Error in afterMethod", e); } diff --git a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/FieldAccessRecorder.java b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/FieldAccessRecorder.java index 2026c13..12cc87f 100644 --- a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/FieldAccessRecorder.java +++ b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/FieldAccessRecorder.java @@ -33,7 +33,6 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -import java.util.Stack; public class FieldAccessRecorder { private static final Logger LOG = new Logger(FieldAccessRecorder.class); @@ -54,7 +53,8 @@ public class FieldAccessRecorder { needCombined = combined; } - public static void write(String fromClass, String fromMethod, int lineNumber, String fieldClass, String fieldName) { + public static void write(String fromClass, String fromMethod, int lineNumber, String fieldClass, + String fieldName) { try { LOG.trace("Write to {}::{} from {}::{}", fieldClass, fieldName, fromClass, fromMethod); long threadId = Thread.currentThread().getId(); @@ -63,13 +63,15 @@ public static void write(String fromClass, String fromMethod, int lineNumber, St graph = new DataDependenceGraph(threadId); GRAPHS.put(threadId, graph); } - graph.addWrite(new StackItem(fromClass, fromMethod, lineNumber), fieldClass + "::" + fieldName); + graph.addWrite(new StackItem(fromClass, fromMethod, lineNumber, false), + fieldClass + "::" + fieldName); } catch (Exception e) { LOG.error("Error in write", e); } } - public static void read(String fromClass, String fromMethod, int lineNumber, String fieldClass, String fieldName) { + public static void read(String fromClass, String fromMethod, int lineNumber, String fieldClass, + String fieldName) { try { LOG.trace("Read to {}::{} from {}::{}", fieldClass, fieldName, fromClass, fromMethod); long threadId = Thread.currentThread().getId(); @@ -80,10 +82,10 @@ public static void read(String fromClass, String fromMethod, int lineNumber, Str } if (needCombined) { CallGraph callGraph = CallRecorder.GRAPHS.get(threadId); - graph.addRead(new StackItem(fromClass, fromMethod, lineNumber), fieldClass + "::" + + graph.addRead(new StackItem(fromClass, fromMethod, lineNumber, false), fieldClass + "::" + fieldName, callGraph); } else { - graph.addRead(new StackItem(fromClass, fromMethod, lineNumber), fieldClass + "::" + + graph.addRead(new StackItem(fromClass, fromMethod, lineNumber, false), fieldClass + "::" + fieldName, null); } } catch (Exception e) { diff --git a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/Tracer.java b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/Tracer.java index 03d109f..8b05c74 100644 --- a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/Tracer.java +++ b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/Tracer.java @@ -62,12 +62,14 @@ public class Tracer implements ClassFileTransformer { * @throws IOException io error * @throws IllegalAccessException problem loading the config options */ - public static void premain(String argument, Instrumentation instrumentation) throws IOException, IllegalAccessException { + public static void premain(String argument, Instrumentation instrumentation) + throws IOException, IllegalAccessException { ShutdownHook.init(); if (argument != null) { new ConfigReader(new File(argument)).read(); } else { - System.err.println("You did not specify a config file. Will use the default config options instead."); + System.err.println( + "You did not specify a config file. Will use the default config options instead."); } @@ -106,7 +108,9 @@ public byte[] transform(ClassLoader loader, String className, Class clazz, if (enhanceClass) { byte[] b = enhanceClass(bytes); - if (b != null) return b; + if (b != null) { + return b; + } } return bytes; } @@ -184,9 +188,23 @@ void enhanceMethod(CtBehavior method, String className) method.instrument(fieldTracer); } + boolean isTest = false; + MethodInfo info = method.getMethodInfo2(); + if (info.isMethod() && !Modifier.isStatic(info.getAccessFlags())) { + // FIXME check if class extends Testcase + isTest = checkTestAnnotation(method); + } + int lineNumber = getLineNumber(method); - String args = '"' + className + '"' + ',' + '"' + mName + '"' + ',' + lineNumber; + String args; + if (isTest) { + LOG.debug("subtest detection enabled on {}::{}", className, mName); + args = + "getClass().getCanonicalName()" + ',' + '"' + mName + '"' + ',' + lineNumber + ',' + true; + } else { + args = '"' + className + '"' + ',' + '"' + mName + '"' + ',' + lineNumber + ',' + false; + } String srcBefore = "com.dkarv.jdcallgraph.CallRecorder.beforeMethod(" + args + ");"; String srcAfter = "com.dkarv.jdcallgraph.CallRecorder.afterMethod(" + args + ");"; @@ -199,6 +217,26 @@ void enhanceMethod(CtBehavior method, String className) //} } + private static boolean checkTestAnnotation(CtBehavior method) { + MethodInfo mi = method.getMethodInfo2(); + AnnotationsAttribute attr = + ((AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.invisibleTag)); + if (attr != null) { + javassist.bytecode.annotation.Annotation anno = attr.getAnnotation("org.junit.Test"); + if (anno != null) { + return true; + } + } + + + attr = ((AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.visibleTag)); + if (attr == null) { + return false; + } + javassist.bytecode.annotation.Annotation anno = attr.getAnnotation("org.junit.Test"); + return anno != null; + } + public static int getLineNumber(CtBehavior method) { return method.getMethodInfo().getLineNumber(0); } diff --git a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/callgraph/CallGraph.java b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/callgraph/CallGraph.java index acc92d8..662d5ba 100644 --- a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/callgraph/CallGraph.java +++ b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/callgraph/CallGraph.java @@ -124,11 +124,11 @@ public void returned(StackItem method) throws IOException { } } if (removed != 1) { - LOG.error("Error when method {} returned:", method); - LOG.error("Removed {} entries. Stack trace {}", removed, trace); + LOG.warn("Error when method {} returned:", method); + LOG.warn("Removed {} entries. Stack trace {}", removed, trace); } if (!found) { - LOG.error("Couldn't find the returned method call on stack"); + LOG.warn("Couldn't find the returned method call on stack"); } if (calls.isEmpty()) { for (GraphWriter w : writers) { diff --git a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/util/StackItem.java b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/util/StackItem.java index 5c7de46..aaf9c4b 100644 --- a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/util/StackItem.java +++ b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/util/StackItem.java @@ -30,16 +30,24 @@ public class StackItem { private final String methodName; private final int lineNumber; + private final boolean test; + private final String formatted; - public StackItem(String className, String methodName, int lineNumber) { + public StackItem(String className, String methodName, int lineNumber, boolean test) { this.className = className; this.methodName = methodName; this.lineNumber = lineNumber; + this.test = test; + this.formatted = Formatter.format(this); } + public StackItem(String className, String methodName, int lineNumber) { + this(className, methodName, lineNumber, false); + } + public String getClassName() { return className; } @@ -52,6 +60,10 @@ public int getLineNumber() { return lineNumber; } + public boolean isTest() { + return test; + } + @Override public String toString() { return formatted; diff --git a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/writer/CsvCoverageFileWriter.java b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/writer/CsvCoverageFileWriter.java index b3f4fa3..8b4e7c9 100644 --- a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/writer/CsvCoverageFileWriter.java +++ b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/writer/CsvCoverageFileWriter.java @@ -44,17 +44,21 @@ public void start(String identifier) throws IOException { @Override public void node(StackItem method) throws IOException { - currentItem = method; + if (method.isTest()) { + currentItem = method; + } } @Override public void edge(StackItem from, StackItem to) throws IOException { - Set list = usedIn.get(to); - if (list == null) { - list = new HashSet<>(); - usedIn.put(to, list); + if (currentItem != null) { + Set list = usedIn.get(to); + if (list == null) { + list = new HashSet<>(); + usedIn.put(to, list); + } + list.add(currentItem); } - list.add(currentItem); } @Override diff --git a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/writer/CsvTraceFileWriter.java b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/writer/CsvTraceFileWriter.java index d201c31..8587a79 100644 --- a/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/writer/CsvTraceFileWriter.java +++ b/jdcallgraph/src/main/java/com/dkarv/jdcallgraph/writer/CsvTraceFileWriter.java @@ -31,6 +31,7 @@ public class CsvTraceFileWriter implements GraphWriter { FileWriter writer; + private boolean interested = true; private final Set trace = new HashSet<>(); @@ -44,13 +45,16 @@ public void start(String identifier) throws IOException { @Override public void node(StackItem method) throws IOException { - writer.append(method.toString()); trace.clear(); + interested = method.isTest(); + if (interested) { + writer.append(method.toString()); + } } @Override public void edge(StackItem from, StackItem to) throws IOException { - if (!trace.contains(to)) { + if (interested && !trace.contains(to)) { writer.append(';'); writer.append(to.toString()); trace.add(to); @@ -64,7 +68,9 @@ public void edge(StackItem from, StackItem to, String label) throws IOException @Override public void end() throws IOException { - writer.append('\n'); + if (interested) { + writer.append('\n'); + } } @Override diff --git a/jdcallgraph/src/test/java/com/dkarv/jdcallgraph/EnhanceMethodTest.java b/jdcallgraph/src/test/java/com/dkarv/jdcallgraph/EnhanceMethodTest.java index 80df383..5d7a862 100644 --- a/jdcallgraph/src/test/java/com/dkarv/jdcallgraph/EnhanceMethodTest.java +++ b/jdcallgraph/src/test/java/com/dkarv/jdcallgraph/EnhanceMethodTest.java @@ -36,7 +36,7 @@ public void before() throws IOException, NotFoundException, CannotCompileExcepti } private String expected(String cName, String mName) { - String args = '"' + cName + '"' + ',' + '"' + mName + '"' + ',' + lineNumber; + String args = '"' + cName + '"' + ',' + '"' + mName + '"' + ',' + lineNumber + ",false"; return "com.dkarv.jdcallgraph.CallRecorder.beforeMethod(" + args + ");"; }