Dieser Commit ist enthalten in:
Ursprung
3651a3bab2
Commit
25afe9f605
2
pom.xml
2
pom.xml
@ -27,7 +27,7 @@
|
|||||||
<goal>shade</goal>
|
<goal>shade</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<minimizeJar>false</minimizeJar>
|
<minimizeJar>true</minimizeJar>
|
||||||
<transformers>
|
<transformers>
|
||||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
<manifestEntries>
|
<manifestEntries>
|
||||||
|
@ -20,6 +20,7 @@ public class Agent {
|
|||||||
|
|
||||||
public static void agentmain(String args, Instrumentation inst) {
|
public static void agentmain(String args, Instrumentation inst) {
|
||||||
instrumentation = inst;
|
instrumentation = inst;
|
||||||
|
//AsyncProfiler.getInstance("/home/lixfel/libasyncProfiler.so");
|
||||||
switch (args) {
|
switch (args) {
|
||||||
case "start":
|
case "start":
|
||||||
start();
|
start();
|
||||||
@ -37,7 +38,7 @@ public class Agent {
|
|||||||
if(shutdownHook != null)
|
if(shutdownHook != null)
|
||||||
return;
|
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");
|
shutdownHook = new Thread(Agent::stop, "SamplerShutdownHook");
|
||||||
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,7 @@ package de.steamwar;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class Method {
|
public class Method {
|
||||||
private static final DecimalFormat df = new DecimalFormat("0.00");
|
private static final DecimalFormat df = new DecimalFormat("0.00");
|
||||||
@ -16,43 +14,21 @@ public class Method {
|
|||||||
private final int id;
|
private final int id;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
private final int[] samples = new int[3];
|
private final int[] samples = new int[State.values().length];
|
||||||
private final HashMap<Method, Integer> calls = new HashMap<>();
|
private final HashMap<Method, Integer> calls = new HashMap<>();
|
||||||
|
|
||||||
public Method(Profile profile, Map<String, ObfHelper.ClassMapping> mappings, String name) {
|
public Method(Profile profile, Map<String, ObfHelper.ClassMapping> mappings, String name) {
|
||||||
this.profile = profile;
|
this.profile = profile;
|
||||||
this.id = idCounter++;
|
this.id = idCounter++;
|
||||||
|
|
||||||
int methodDivisor = name.lastIndexOf(".");
|
try {
|
||||||
if(methodDivisor != -1) {
|
name = javaPrettify(mappings, name);
|
||||||
String className = name.substring(0, methodDivisor);
|
} catch (IllegalArgumentException e) {
|
||||||
String methodDefinition = name.substring(methodDivisor+1).replace('|', ';');
|
if(name.startsWith("/")) { // native lib
|
||||||
String methodName = methodDefinition.split("\\(", 2)[0];
|
name = name.substring(name.lastIndexOf('/') + 1);
|
||||||
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(" ").append(className.replace('$', '.')).append(".").append(methodName).append("(");
|
|
||||||
formatJvmTypes(mappings, builder, methodArgs);
|
|
||||||
builder.append(")");
|
|
||||||
|
|
||||||
name = builder.toString();
|
|
||||||
}
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +36,7 @@ public class Method {
|
|||||||
return Arrays.stream(samples).sum();
|
return Arrays.stream(samples).sum();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean filtered() {
|
public boolean filtered() {
|
||||||
return Arrays.stream(samples).sum() < Profile.FILTER;
|
return Arrays.stream(samples).sum() < Profile.FILTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,32 +60,67 @@ public class Method {
|
|||||||
return df.format(value * Profile.SAMPLING_SPEED / 1e9);
|
return df.format(value * Profile.SAMPLING_SPEED / 1e9);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void toDot(OutputStreamWriter writer) throws IOException {
|
public void prune() {
|
||||||
if (filtered())
|
calls.keySet().removeIf(Method::filtered);
|
||||||
return;
|
calls.values().removeIf(s -> s < Profile.FILTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toDot(OutputStreamWriter writer) throws IOException {
|
||||||
int waiting = samples[State.WAITING.ordinal()];
|
int waiting = samples[State.WAITING.ordinal()];
|
||||||
int runnable = samples[State.RUNNING.ordinal()];
|
int runnable = samples[State.RUNNING.ordinal()];
|
||||||
int blocked = samples[State.BLOCKED.ordinal()];
|
int io = samples[State.IO.ordinal()];
|
||||||
int total = waiting + runnable + blocked;
|
int time = samples[State.TIME.ordinal()];
|
||||||
|
int total = waiting + runnable + io + time;
|
||||||
|
|
||||||
int r = (255 * blocked + 192 * waiting) / total;
|
int r = ( 255 * waiting + 192 * time) / total;
|
||||||
int g = (255 * runnable + 192 * waiting) / total;
|
int g = (255 * runnable + 64 * waiting + 192 * io + 192 * time) / total;
|
||||||
int b = (192 * waiting) / total;
|
int b = ( 64 * waiting + 255 * io + 192 * time) / total;
|
||||||
int a = (int) (255 * Math.sqrt(total / (double) profile.getSampleRuns()));
|
int a = (int) (255 * Math.sqrt(total / (double) profile.getSampleRuns()));
|
||||||
if (a > 255)
|
if (a > 255)
|
||||||
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<Method, Integer> entry : calls.entrySet()) {
|
for (Map.Entry<Method, Integer> 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");
|
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<String, ObfHelper.ClassMapping> 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<String, ObfHelper.ClassMapping> mappings, StringBuilder builder, String jvmType) {
|
private void formatJvmTypes(Map<String, ObfHelper.ClassMapping> mappings, StringBuilder builder, String jvmType) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while(i < jvmType.length()) {
|
while(i < jvmType.length()) {
|
||||||
|
@ -4,35 +4,33 @@ import java.io.File;
|
|||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class Profile {
|
public class Profile {
|
||||||
|
|
||||||
public static final long SAMPLING_SPEED = 1000000;
|
public static final long SAMPLING_SPEED = 1000000;
|
||||||
public static final int FILTER = 100;
|
public static final int FILTER = 100;
|
||||||
|
|
||||||
private static final HashSet<String> waitingMethods = new HashSet<>();
|
private static final HashMap<String, State> stateMethods = new HashMap<>();
|
||||||
static {
|
static {
|
||||||
waitingMethods.add("jdk.internal.misc.Unsafe.park(ZJ)V");
|
stateMethods.put("jdk.internal.misc.Unsafe.park(ZJ)V", State.TIME);
|
||||||
waitingMethods.add("java.lang.Thread.sleep(J)V");
|
stateMethods.put("java.lang.Thread.sleep(J)V", State.TIME);
|
||||||
waitingMethods.add("java.lang.Object.wait(J)V");
|
|
||||||
waitingMethods.add("java.lang.Object.wait(JI)V");
|
stateMethods.put("java.lang.Object.wait(J)V", State.WAITING);
|
||||||
waitingMethods.add("java.lang.ref.Reference.waitForReferencePendingList()V");
|
stateMethods.put("java.lang.Object.wait(JI)V", State.WAITING);
|
||||||
waitingMethods.add("accept");
|
stateMethods.put("java.lang.ref.Reference.waitForReferencePendingList()V", State.WAITING);
|
||||||
waitingMethods.add("poll");
|
stateMethods.put("com.ibm.lang.management.internal.MemoryNotificationThread.processNotificationLoop()V", State.WAITING);
|
||||||
waitingMethods.add("com.ibm.lang.management.internal.MemoryNotificationThread.processNotificationLoop()V");
|
|
||||||
waitingMethods.add("openj9.internal.tools.attach.target.IPC.waitSemaphore()I");
|
stateMethods.put("sun.nio.ch.SocketDispatcher.read0(Ljava/io/FileDescriptor|JI)I", State.IO);
|
||||||
waitingMethods.add("net.minecrell.terminalconsole.SimpleTerminalConsole.readCommands(Ljava/io/InputStream|)V");
|
stateMethods.put("io.netty.channel.epoll.Native.epollWait(IJII)I", State.IO);
|
||||||
waitingMethods.add("sun.nio.ch.SocketDispatcher.read0(Ljava/io/FileDescriptor|JI)I");
|
stateMethods.put("openj9.internal.tools.attach.target.IPC.waitSemaphore()I", State.IO);
|
||||||
waitingMethods.add("io.netty.channel.epoll.Native.epollWait(IJII)I");
|
stateMethods.put("java.io.FileInputStream.readBytes([BII)I", State.IO);
|
||||||
}
|
}
|
||||||
//private static final HashSet<String> blockingMethods = new HashSet<>(); For later if possible
|
|
||||||
private static final HashSet<String> omittedMethods = new HashSet<>();
|
private static final HashSet<String> omittedMethods = new HashSet<>();
|
||||||
static {
|
static {
|
||||||
omittedMethods.add("/usr/lib/libc.so.6");
|
|
||||||
omittedMethods.add("java.lang.Thread.run()V");
|
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.run()V");
|
||||||
omittedMethods.add("java.util.concurrent.ForkJoinWorkerThread.runWorker(Ljava/util/concurrent/ForkJoinPool$WorkQueue|)V");
|
omittedMethods.add("java.util.concurrent.ForkJoinWorkerThread.runWorker(Ljava/util/concurrent/ForkJoinPool$WorkQueue|)V");
|
||||||
omittedMethods.add("java.util.concurrent.ThreadPoolExecutor$Worker.run()V");
|
omittedMethods.add("java.util.concurrent.ThreadPoolExecutor$Worker.run()V");
|
||||||
@ -40,7 +38,7 @@ public class Profile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final HashMap<String, Method> methods = new HashMap<>();
|
private final HashMap<String, Method> methods = new HashMap<>();
|
||||||
private final HashMap<String, Integer> threadSamples = new HashMap<>();
|
private final HashMap<Method, Integer> threads = new HashMap<>();
|
||||||
|
|
||||||
public Profile(Map<String, ObfHelper.ClassMapping> mappings, String profile) {
|
public Profile(Map<String, ObfHelper.ClassMapping> mappings, String profile) {
|
||||||
HashSet<String> previousIds = new HashSet<>();
|
HashSet<String> previousIds = new HashSet<>();
|
||||||
@ -57,10 +55,7 @@ public class Profile {
|
|||||||
State state = State.RUNNING;
|
State state = State.RUNNING;
|
||||||
|
|
||||||
for(String name : names) {
|
for(String name : names) {
|
||||||
if (waitingMethods.contains(name)) {
|
state = stateMethods.getOrDefault(name, state);
|
||||||
state = State.WAITING;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Method calling = null;
|
Method calling = null;
|
||||||
@ -75,28 +70,32 @@ public class Profile {
|
|||||||
calling = method;
|
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() {
|
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() {
|
public void save() {
|
||||||
|
prune();
|
||||||
|
|
||||||
File output = new File(System.getProperty("user.home"), "samples.dot");
|
File output = new File(System.getProperty("user.home"), "samples.dot");
|
||||||
try(OutputStreamWriter writer = new FileWriter(output)) {
|
try(OutputStreamWriter writer = new FileWriter(output)) {
|
||||||
|
//\ngraph [splines=true overlap=false];
|
||||||
writer.append("digraph {\nnode [shape=plaintext,style=\"filled\", margin=0.1];\n");
|
writer.append("digraph {\ngraph [splines=true,overlap=false,searchsize=2];\nnode [shape=plaintext,style=\"filled\",margin=0.1];\n");
|
||||||
|
|
||||||
for(Method method : methods.values()) {
|
for(Method method : methods.values()) {
|
||||||
|
|
||||||
method.toDot(writer);
|
method.toDot(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.append("}");
|
writer.append("}");
|
||||||
|
|
||||||
//writer.flush();
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -3,5 +3,6 @@ package de.steamwar;
|
|||||||
public enum State {
|
public enum State {
|
||||||
RUNNING,
|
RUNNING,
|
||||||
WAITING,
|
WAITING,
|
||||||
BLOCKED
|
TIME,
|
||||||
|
IO
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren