diff --git a/pom.xml b/pom.xml
index 8a8b96e..cca24b1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,7 +27,7 @@
shade
- false
+ true
diff --git a/src/main/java/de/steamwar/Agent.java b/src/main/java/de/steamwar/Agent.java
index 88e7a35..ad8324d 100644
--- a/src/main/java/de/steamwar/Agent.java
+++ b/src/main/java/de/steamwar/Agent.java
@@ -20,6 +20,7 @@ public class Agent {
public static void agentmain(String args, Instrumentation inst) {
instrumentation = inst;
+ //AsyncProfiler.getInstance("/home/lixfel/libasyncProfiler.so");
switch (args) {
case "start":
start();
@@ -37,7 +38,7 @@ public class Agent {
if(shutdownHook != null)
return;
- callProfiler("start,event=wall,threads,interval=1000000,alluser"); // 1mio ns = 1ms
+ callProfiler("start,event=wall,threads,interval=1000000,cstack=fp"); // 1mio ns = 1ms
shutdownHook = new Thread(Agent::stop, "SamplerShutdownHook");
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
diff --git a/src/main/java/de/steamwar/Method.java b/src/main/java/de/steamwar/Method.java
index 543700d..1572d80 100644
--- a/src/main/java/de/steamwar/Method.java
+++ b/src/main/java/de/steamwar/Method.java
@@ -3,9 +3,7 @@ package de.steamwar;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.DecimalFormat;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
public class Method {
private static final DecimalFormat df = new DecimalFormat("0.00");
@@ -16,43 +14,21 @@ public class Method {
private final int id;
private final String name;
- private final int[] samples = new int[3];
+ private final int[] samples = new int[State.values().length];
private final HashMap calls = new HashMap<>();
public Method(Profile profile, Map mappings, String name) {
this.profile = profile;
this.id = idCounter++;
- int methodDivisor = name.lastIndexOf(".");
- if(methodDivisor != -1) {
- String className = name.substring(0, methodDivisor);
- String methodDefinition = name.substring(methodDivisor+1).replace('|', ';');
- String methodName = methodDefinition.split("\\(", 2)[0];
- String methodArgs = methodDefinition.split("\\(", 2)[1].split("\\)", 2)[0];
- String methodReturn = methodDefinition.split("\\)", 2)[1];
-
- ObfHelper.ClassMapping mapping = mappings.get(className);
- if(mapping != null) {
- className = mapping.mojangName;
- methodName = mapping.methodsByObf.getOrDefault(methodDefinition, methodName);
+ try {
+ name = javaPrettify(mappings, name);
+ } catch (IllegalArgumentException e) {
+ if(name.startsWith("/")) { // native lib
+ name = name.substring(name.lastIndexOf('/') + 1);
}
-
- if(className.contains("$$Lambda$")) {
- className = className.split("\\$\\$Lambda\\$", 2)[0] + ".λ";
- }
-
- if(methodName.startsWith("lambda$")) {
- methodName = methodName.split("\\$", 2)[1].replace('$', 'λ');
- }
-
- StringBuilder builder = new StringBuilder();
- formatJvmTypes(mappings, builder, methodReturn);
- builder.append(" ").append(className.replace('$', '.')).append(".").append(methodName).append("(");
- formatJvmTypes(mappings, builder, methodArgs);
- builder.append(")");
-
- name = builder.toString();
}
+
this.name = name;
}
@@ -60,7 +36,7 @@ public class Method {
return Arrays.stream(samples).sum();
}
- private boolean filtered() {
+ public boolean filtered() {
return Arrays.stream(samples).sum() < Profile.FILTER;
}
@@ -84,32 +60,67 @@ public class Method {
return df.format(value * Profile.SAMPLING_SPEED / 1e9);
}
- public void toDot(OutputStreamWriter writer) throws IOException {
- if (filtered())
- return;
+ public void prune() {
+ calls.keySet().removeIf(Method::filtered);
+ calls.values().removeIf(s -> s < Profile.FILTER);
+ }
+ public void toDot(OutputStreamWriter writer) throws IOException {
int waiting = samples[State.WAITING.ordinal()];
int runnable = samples[State.RUNNING.ordinal()];
- int blocked = samples[State.BLOCKED.ordinal()];
- int total = waiting + runnable + blocked;
+ int io = samples[State.IO.ordinal()];
+ int time = samples[State.TIME.ordinal()];
+ int total = waiting + runnable + io + time;
- int r = (255 * blocked + 192 * waiting) / total;
- int g = (255 * runnable + 192 * waiting) / total;
- int b = (192 * waiting) / total;
+ int r = ( 255 * waiting + 192 * time) / total;
+ int g = (255 * runnable + 64 * waiting + 192 * io + 192 * time) / total;
+ int b = ( 64 * waiting + 255 * io + 192 * time) / total;
int a = (int) (255 * Math.sqrt(total / (double) profile.getSampleRuns()));
if (a > 255)
a = 255;
- writer.append(String.valueOf(id)).append(" [fillcolor=\"#").append(String.format("%02X", r)).append(String.format("%02X", g)).append(String.format("%02X", b)).append(String.format("%02X", a)).append("\",label=\"").append(name).append("\\n").append(time(ownSampleRuns())).append("s ").append(totalPercentage(ownSampleRuns())).append("%\\nR").append(percentage(runnable)).append("% B").append(percentage(blocked)).append("% W").append(percentage(waiting)).append("%\"];\n");
+ writer.append(String.valueOf(id)).append(" [fillcolor=\"#").append(String.format("%02X", r)).append(String.format("%02X", g)).append(String.format("%02X", b)).append(String.format("%02X", a)).append("\",label=\"").append(name).append("\\n").append(time(ownSampleRuns())).append("s ").append(totalPercentage(ownSampleRuns())).append("% R").append(percentage(runnable)).append("% T").append(percentage(time)).append("% I").append(percentage(io)).append("% W").append(percentage(waiting)).append("%\"];\n");
for (Map.Entry entry : calls.entrySet()) {
- if (entry.getKey().filtered() || entry.getValue() < Profile.FILTER)
- continue;
-
writer.append(String.valueOf(id)).append(" -> ").append(String.valueOf(entry.getKey().id)).append(" [label=\"").append(time(entry.getValue())).append("s\\n").append(percentage(entry.getValue())).append("%\",weight=").append(String.valueOf(entry.getValue())).append("];\n");
}
}
+ private String javaPrettify(Map mappings, String name) {
+ try {
+ int methodDivisor = name.lastIndexOf(".");
+ String className = name.substring(0, methodDivisor);
+ String methodDefinition = name.substring(methodDivisor + 1).replace('|', ';');
+ String methodName = methodDefinition.split("\\(", 2)[0];
+ String methodArgs = methodDefinition.split("\\(", 2)[1].split("\\)", 2)[0];
+ String methodReturn = methodDefinition.split("\\)", 2)[1];
+
+ ObfHelper.ClassMapping mapping = mappings.get(className);
+ if (mapping != null) {
+ className = mapping.mojangName;
+ methodName = mapping.methodsByObf.getOrDefault(methodDefinition, methodName);
+ }
+
+ if (className.contains("$$Lambda$")) {
+ className = className.split("\\$\\$Lambda\\$", 2)[0] + ".λ";
+ }
+
+ if (methodName.startsWith("lambda$")) {
+ methodName = methodName.split("\\$", 2)[1].replace('$', 'λ');
+ }
+
+ StringBuilder builder = new StringBuilder();
+ formatJvmTypes(mappings, builder, methodReturn);
+ builder.append("\\n").append(className.replace('$', '.')).append(".").append(methodName).append("\\n(");
+ formatJvmTypes(mappings, builder, methodArgs);
+ builder.append(")");
+
+ return builder.toString();
+ } catch (Throwable t) {
+ throw new IllegalArgumentException(t);
+ }
+ }
+
private void formatJvmTypes(Map mappings, StringBuilder builder, String jvmType) {
int i = 0;
while(i < jvmType.length()) {
diff --git a/src/main/java/de/steamwar/Profile.java b/src/main/java/de/steamwar/Profile.java
index a79a9ac..9f8139f 100644
--- a/src/main/java/de/steamwar/Profile.java
+++ b/src/main/java/de/steamwar/Profile.java
@@ -4,35 +4,33 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
+import java.util.*;
public class Profile {
public static final long SAMPLING_SPEED = 1000000;
public static final int FILTER = 100;
- private static final HashSet waitingMethods = new HashSet<>();
+ private static final HashMap stateMethods = new HashMap<>();
static {
- waitingMethods.add("jdk.internal.misc.Unsafe.park(ZJ)V");
- waitingMethods.add("java.lang.Thread.sleep(J)V");
- waitingMethods.add("java.lang.Object.wait(J)V");
- waitingMethods.add("java.lang.Object.wait(JI)V");
- waitingMethods.add("java.lang.ref.Reference.waitForReferencePendingList()V");
- waitingMethods.add("accept");
- waitingMethods.add("poll");
- waitingMethods.add("com.ibm.lang.management.internal.MemoryNotificationThread.processNotificationLoop()V");
- waitingMethods.add("openj9.internal.tools.attach.target.IPC.waitSemaphore()I");
- waitingMethods.add("net.minecrell.terminalconsole.SimpleTerminalConsole.readCommands(Ljava/io/InputStream|)V");
- waitingMethods.add("sun.nio.ch.SocketDispatcher.read0(Ljava/io/FileDescriptor|JI)I");
- waitingMethods.add("io.netty.channel.epoll.Native.epollWait(IJII)I");
+ stateMethods.put("jdk.internal.misc.Unsafe.park(ZJ)V", State.TIME);
+ stateMethods.put("java.lang.Thread.sleep(J)V", State.TIME);
+
+ stateMethods.put("java.lang.Object.wait(J)V", State.WAITING);
+ stateMethods.put("java.lang.Object.wait(JI)V", State.WAITING);
+ stateMethods.put("java.lang.ref.Reference.waitForReferencePendingList()V", State.WAITING);
+ stateMethods.put("com.ibm.lang.management.internal.MemoryNotificationThread.processNotificationLoop()V", State.WAITING);
+
+ stateMethods.put("sun.nio.ch.SocketDispatcher.read0(Ljava/io/FileDescriptor|JI)I", State.IO);
+ stateMethods.put("io.netty.channel.epoll.Native.epollWait(IJII)I", State.IO);
+ stateMethods.put("openj9.internal.tools.attach.target.IPC.waitSemaphore()I", State.IO);
+ stateMethods.put("java.io.FileInputStream.readBytes([BII)I", State.IO);
}
- //private static final HashSet blockingMethods = new HashSet<>(); For later if possible
+
private static final HashSet omittedMethods = new HashSet<>();
static {
- omittedMethods.add("/usr/lib/libc.so.6");
omittedMethods.add("java.lang.Thread.run()V");
+ omittedMethods.add("java.util.concurrent.FutureTask.run()V");
omittedMethods.add("java.util.concurrent.ForkJoinWorkerThread.run()V");
omittedMethods.add("java.util.concurrent.ForkJoinWorkerThread.runWorker(Ljava/util/concurrent/ForkJoinPool$WorkQueue|)V");
omittedMethods.add("java.util.concurrent.ThreadPoolExecutor$Worker.run()V");
@@ -40,7 +38,7 @@ public class Profile {
}
private final HashMap methods = new HashMap<>();
- private final HashMap threadSamples = new HashMap<>();
+ private final HashMap threads = new HashMap<>();
public Profile(Map mappings, String profile) {
HashSet previousIds = new HashSet<>();
@@ -57,10 +55,7 @@ public class Profile {
State state = State.RUNNING;
for(String name : names) {
- if (waitingMethods.contains(name)) {
- state = State.WAITING;
- break;
- }
+ state = stateMethods.getOrDefault(name, state);
}
Method calling = null;
@@ -75,28 +70,32 @@ public class Profile {
calling = method;
}
- threadSamples.compute(names[0], (threadName, previous) -> previous == null ? samples : previous + samples);
+ threads.compute(calling, (thread, previous) -> previous == null ? samples : previous + samples);
}
}
public int getSampleRuns() {
- return threadSamples.values().stream().max(Integer::compareTo).orElse(0);
+ return threads.values().stream().max(Integer::compareTo).orElse(0);
+ }
+
+ public void prune() {
+ methods.values().removeIf(Method::filtered);
+ methods.values().forEach(Method::prune);
}
public void save() {
+ prune();
+
File output = new File(System.getProperty("user.home"), "samples.dot");
try(OutputStreamWriter writer = new FileWriter(output)) {
-
- writer.append("digraph {\nnode [shape=plaintext,style=\"filled\", margin=0.1];\n");
+ //\ngraph [splines=true overlap=false];
+ writer.append("digraph {\ngraph [splines=true,overlap=false,searchsize=2];\nnode [shape=plaintext,style=\"filled\",margin=0.1];\n");
for(Method method : methods.values()) {
-
method.toDot(writer);
}
writer.append("}");
-
- //writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
diff --git a/src/main/java/de/steamwar/State.java b/src/main/java/de/steamwar/State.java
index e915a00..3004ddc 100644
--- a/src/main/java/de/steamwar/State.java
+++ b/src/main/java/de/steamwar/State.java
@@ -3,5 +3,6 @@ package de.steamwar;
public enum State {
RUNNING,
WAITING,
- BLOCKED
+ TIME,
+ IO
}