From 25afe9f60581b82dea1593deb43ea32bfa361f68 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 29 Nov 2022 11:49:57 +0100 Subject: [PATCH] Current state --- pom.xml | 2 +- src/main/java/de/steamwar/Agent.java | 3 +- src/main/java/de/steamwar/Method.java | 101 ++++++++++++++----------- src/main/java/de/steamwar/Profile.java | 59 +++++++-------- src/main/java/de/steamwar/State.java | 3 +- 5 files changed, 90 insertions(+), 78 deletions(-) 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 }