diff --git a/src/main/java/perfix/Agent.java b/src/main/java/perfix/Agent.java old mode 100644 new mode 100755 index a5c51a7..4b4747c --- a/src/main/java/perfix/Agent.java +++ b/src/main/java/perfix/Agent.java @@ -2,8 +2,13 @@ package perfix; import javassist.*; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; import java.lang.instrument.Instrumentation; +import java.net.ServerSocket; +import java.net.Socket; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -11,33 +16,84 @@ import java.util.List; public class Agent { public static void premain(String agentArgs, Instrumentation inst) { - List excludes = determineExcludes(); + System.out.println("Perfix agent active"); + startListeningOnSocket(); + + List includes = determineIncludes(); inst.addTransformer((classLoader, resource, aClass, protectionDomain, uninstrumentedByteCode) -> { - if (!shouldExclude(resource, excludes).get()) { - System.out.println("instrumenting "+resource); + if (!isInnerClass(resource) && shouldInclude(resource, includes)) { +// System.out.println("instrumenting " + resource); + try { - return instrumentMethod(resource); + byte[] instrumentedBytecode = instrumentMethod(resource); + if (instrumentedBytecode != null) { + return instrumentedBytecode; + } } catch (Exception ex) { - ex.printStackTrace(System.err); + //suppress + } } return uninstrumentedByteCode; }); } - private static byte[] instrumentMethod(String resource) throws NotFoundException, IOException, CannotCompileException { + private static void startListeningOnSocket() { + try { + ServerSocket serverSocket = new ServerSocket(2048); + new Thread(() -> { + for (; ; ) { + try { + Socket client = serverSocket.accept(); + + PrintStream out = new PrintStream(client.getOutputStream()); + out.println("press [enter] for report or [q and enter] to quit"); + + BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + if (line.equals("q")) { + try { + client.close(); + break; + } catch (IOException e) { + e.printStackTrace(); + } + } else { + Registry.report(out); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }).start(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static boolean isInnerClass(String resource) { + return resource.contains("$"); + } + + private static byte[] instrumentMethod(String resource) throws + NotFoundException, IOException, CannotCompileException { ClassPool cp = ClassPool.getDefault(); CtClass methodClass = cp.get("perfix.Method"); - - CtClass cc = cp.get(resource.replaceAll("/", ".")); - Arrays.stream(cc.getDeclaredMethods()).forEach(m -> { - instrumentMethod(methodClass, m); - }); - byte[] byteCode = cc.toBytecode(); - cc.detach(); - return byteCode; + CtClass classToInstrument = cp.get(resource.replaceAll("/", ".")); + if (!classToInstrument.isInterface()) { + Arrays.stream(classToInstrument.getDeclaredMethods()).forEach(m -> { + instrumentMethod(methodClass, m); + }); + byte[] byteCode = classToInstrument.toBytecode(); + classToInstrument.detach(); + return byteCode; + } else { + return null; + } } private static void instrumentMethod(CtClass methodClass, CtMethod m) { @@ -46,24 +102,22 @@ public class Agent { m.insertBefore("perfixmethod = perfix.Method.start(\"" + m.getLongName() + "\");"); m.insertAfter("perfixmethod.stop();"); } catch (CannotCompileException e) { - e.printStackTrace(System.err); + throw new RuntimeException(e); } } - private static List determineExcludes() { - List excludes = new ArrayList<>(Arrays.asList(System.getProperty("perfix.excludes").split(","))); - excludes.add("perfix"); - return excludes; + private static List determineIncludes() { + return new ArrayList<>(Arrays.asList(System.getProperty("perfix.includes").split(","))); } - private static BooleanWrapper shouldExclude(String resource, List excludes) { - BooleanWrapper excluded = new BooleanWrapper(false); - excludes.forEach(exclude -> { - if (resource.startsWith(exclude)) { - excluded.set(true); + private static boolean shouldInclude(String resource, List excludes) { + BooleanWrapper included = new BooleanWrapper(false); + excludes.forEach(include -> { + if (resource.startsWith(include)) { + included.set(true); } }); - return excluded; + return included.get(); } static class BooleanWrapper { @@ -80,6 +134,11 @@ public class Agent { boolean get() { return value; } + + @Override + public String toString() { + return Boolean.toString(value); + } } } diff --git a/src/main/java/perfix/Method.java b/src/main/java/perfix/Method.java old mode 100644 new mode 100755 diff --git a/src/main/java/perfix/Registry.java b/src/main/java/perfix/Registry.java old mode 100644 new mode 100755 index d81077e..e12a9d1 --- a/src/main/java/perfix/Registry.java +++ b/src/main/java/perfix/Registry.java @@ -1,5 +1,6 @@ package perfix; +import java.io.PrintStream; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap; @@ -8,26 +9,31 @@ import java.util.concurrent.atomic.LongAdder; public class Registry { static final Map> methods = new ConcurrentHashMap<>(); + private static final double NANO_2_MILLI = 1000000D; static void add(Method method) { methods.computeIfAbsent(method.getName(), key -> new ArrayList<>()).add(method); } - public static void report() { - System.out.println("Invoked methods, by duration desc:"); + public static void report(PrintStream out) { + out.println("Invoked methods, by duration desc:"); + out.println("Method name;#Invocations;Total duration;Average Duration"); sortedMethodsByDuration().forEach((k, report) -> { - System.out.println("method: " + report.name); - System.out.println("\tInvocations: " + report.invocations); - System.out.println("\tTotal duration: " + report.totalDuration + " nanosecs."); - System.out.println("\tAverage duration " + report.average() + " nanosecs."); + out.println(report.name + ";" + report.invocations + ";" + (long)(report.totalDuration / NANO_2_MILLI) + ";" + (long)(report.average() / NANO_2_MILLI)); }); + out.println("----------------------------------------"); } private static SortedMap sortedMethodsByDuration() { SortedMap sortedByTotal = new ConcurrentSkipListMap<>(Comparator.reverseOrder()); methods.forEach((name, measurements) -> { - long totalDuration = measurements.stream().mapToLong(Method::getDuration).sum(); - sortedByTotal.put(totalDuration, new Report(name, measurements.size(), totalDuration)); + +// long totalDuration = measurements.stream().mapToLong(Method::getDuration).sum(); + LongAdder totalDuration = new LongAdder(); + measurements.stream() + .filter(Objects::nonNull) + .forEach(m -> totalDuration.add(m.getDuration())); + sortedByTotal.put(totalDuration.longValue(), new Report(name, measurements.size(), totalDuration.longValue())); }); return sortedByTotal; }