switched to telnet interface

This commit is contained in:
Sander Hautvast 2018-04-06 16:31:37 +02:00
parent d87b77edcb
commit bf6ec2acb6
3 changed files with 98 additions and 33 deletions

103
src/main/java/perfix/Agent.java Normal file → Executable file
View file

@ -2,8 +2,13 @@ package perfix;
import javassist.*; import javassist.*;
import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.lang.instrument.Instrumentation; import java.lang.instrument.Instrumentation;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -11,33 +16,84 @@ import java.util.List;
public class Agent { public class Agent {
public static void premain(String agentArgs, Instrumentation inst) { public static void premain(String agentArgs, Instrumentation inst) {
List<String> excludes = determineExcludes(); System.out.println("Perfix agent active");
startListeningOnSocket();
List<String> includes = determineIncludes();
inst.addTransformer((classLoader, resource, aClass, protectionDomain, uninstrumentedByteCode) -> { inst.addTransformer((classLoader, resource, aClass, protectionDomain, uninstrumentedByteCode) -> {
if (!shouldExclude(resource, excludes).get()) { if (!isInnerClass(resource) && shouldInclude(resource, includes)) {
System.out.println("instrumenting "+resource); // System.out.println("instrumenting " + resource);
try { try {
return instrumentMethod(resource); byte[] instrumentedBytecode = instrumentMethod(resource);
if (instrumentedBytecode != null) {
return instrumentedBytecode;
}
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(System.err); //suppress
} }
} }
return uninstrumentedByteCode; 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(); ClassPool cp = ClassPool.getDefault();
CtClass methodClass = cp.get("perfix.Method"); CtClass methodClass = cp.get("perfix.Method");
CtClass classToInstrument = cp.get(resource.replaceAll("/", "."));
CtClass cc = cp.get(resource.replaceAll("/", ".")); if (!classToInstrument.isInterface()) {
Arrays.stream(cc.getDeclaredMethods()).forEach(m -> { Arrays.stream(classToInstrument.getDeclaredMethods()).forEach(m -> {
instrumentMethod(methodClass, m); instrumentMethod(methodClass, m);
}); });
byte[] byteCode = cc.toBytecode(); byte[] byteCode = classToInstrument.toBytecode();
cc.detach(); classToInstrument.detach();
return byteCode; return byteCode;
} else {
return null;
}
} }
private static void instrumentMethod(CtClass methodClass, CtMethod m) { private static void instrumentMethod(CtClass methodClass, CtMethod m) {
@ -46,24 +102,22 @@ public class Agent {
m.insertBefore("perfixmethod = perfix.Method.start(\"" + m.getLongName() + "\");"); m.insertBefore("perfixmethod = perfix.Method.start(\"" + m.getLongName() + "\");");
m.insertAfter("perfixmethod.stop();"); m.insertAfter("perfixmethod.stop();");
} catch (CannotCompileException e) { } catch (CannotCompileException e) {
e.printStackTrace(System.err); throw new RuntimeException(e);
} }
} }
private static List<String> determineExcludes() { private static List<String> determineIncludes() {
List<String> excludes = new ArrayList<>(Arrays.asList(System.getProperty("perfix.excludes").split(","))); return new ArrayList<>(Arrays.asList(System.getProperty("perfix.includes").split(",")));
excludes.add("perfix");
return excludes;
} }
private static BooleanWrapper shouldExclude(String resource, List<String> excludes) { private static boolean shouldInclude(String resource, List<String> excludes) {
BooleanWrapper excluded = new BooleanWrapper(false); BooleanWrapper included = new BooleanWrapper(false);
excludes.forEach(exclude -> { excludes.forEach(include -> {
if (resource.startsWith(exclude)) { if (resource.startsWith(include)) {
excluded.set(true); included.set(true);
} }
}); });
return excluded; return included.get();
} }
static class BooleanWrapper { static class BooleanWrapper {
@ -80,6 +134,11 @@ public class Agent {
boolean get() { boolean get() {
return value; return value;
} }
@Override
public String toString() {
return Boolean.toString(value);
}
} }
} }

0
src/main/java/perfix/Method.java Normal file → Executable file
View file

22
src/main/java/perfix/Registry.java Normal file → Executable file
View file

@ -1,5 +1,6 @@
package perfix; package perfix;
import java.io.PrintStream;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListMap;
@ -8,26 +9,31 @@ import java.util.concurrent.atomic.LongAdder;
public class Registry { public class Registry {
static final Map<String, List<Method>> methods = new ConcurrentHashMap<>(); static final Map<String, List<Method>> methods = new ConcurrentHashMap<>();
private static final double NANO_2_MILLI = 1000000D;
static void add(Method method) { static void add(Method method) {
methods.computeIfAbsent(method.getName(), key -> new ArrayList<>()).add(method); methods.computeIfAbsent(method.getName(), key -> new ArrayList<>()).add(method);
} }
public static void report() { public static void report(PrintStream out) {
System.out.println("Invoked methods, by duration desc:"); out.println("Invoked methods, by duration desc:");
out.println("Method name;#Invocations;Total duration;Average Duration");
sortedMethodsByDuration().forEach((k, report) -> { sortedMethodsByDuration().forEach((k, report) -> {
System.out.println("method: " + report.name); out.println(report.name + ";" + report.invocations + ";" + (long)(report.totalDuration / NANO_2_MILLI) + ";" + (long)(report.average() / NANO_2_MILLI));
System.out.println("\tInvocations: " + report.invocations);
System.out.println("\tTotal duration: " + report.totalDuration + " nanosecs.");
System.out.println("\tAverage duration " + report.average() + " nanosecs.");
}); });
out.println("----------------------------------------");
} }
private static SortedMap<Long, Report> sortedMethodsByDuration() { private static SortedMap<Long, Report> sortedMethodsByDuration() {
SortedMap<Long, Report> sortedByTotal = new ConcurrentSkipListMap<>(Comparator.reverseOrder()); SortedMap<Long, Report> sortedByTotal = new ConcurrentSkipListMap<>(Comparator.reverseOrder());
methods.forEach((name, measurements) -> { 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; return sortedByTotal;
} }