org.gridkit.jvmtool.hflame.JsonFlameDataSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sjk-hflame Show documentation
Show all versions of sjk-hflame Show documentation
HTML based flame graph visualizer
/**
* Copyright 2018 Alexey Ragozin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gridkit.jvmtool.hflame;
import java.lang.Thread.State;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.gridkit.jvmtool.codec.stacktrace.ThreadSnapshotEvent;
import org.gridkit.jvmtool.stacktrace.StackFrameList;
import org.gridkit.jvmtool.stacktrace.analytics.Calculators;
import org.gridkit.jvmtool.stacktrace.analytics.WeigthCalculator;
public class JsonFlameDataSet {
private final Map frameSet = new LinkedHashMap();
private final Map threads = new TreeMap();
private final List pallete = new ArrayList();
private FrameColorChooser colorer;
private FrameFormater formater;
private WeigthCalculator calc;
public JsonFlameDataSet() {
this.colorer = new DefaultFrameColorChooser();
this.formater = new DefaultFrameFormater();
this.calc = Calculators.SAMPLES;
intern(State.RUNNABLE);
intern(State.BLOCKED);
intern(State.WAITING);
intern(State.TIMED_WAITING);
}
public void setWeightCalculator(WeigthCalculator calc) {
this.calc = calc;
}
private int intern(String frame) {
if (frameSet.containsKey(frame)) {
return frameSet.get(frame);
}
else {
int n = frameSet.size();
frameSet.put(frame, n);
pallete.add(toColorLiteral(colorer.getFrameColor(frame)));
return n;
}
}
private int intern(State state) {
switch (state) {
case BLOCKED:
return intern("(BLOCKED)");
case NEW:
return intern("(NEW)");
case RUNNABLE:
return intern("(RUNNABLE)");
case TERMINATED:
return intern("(TERMINATED)");
case TIMED_WAITING:
return intern("(TIMED_WAITING)");
case WAITING:
return intern("(WAITING)");
default:
return intern("(???)");
}
}
private String toColorLiteral(int color) {
if (color < 0) {
return "null";
}
else {
String hex = Integer.toHexString(color + 0x1000000);
hex = hex.substring(hex.length() - 6);
return "\"#" + hex + "\"";
}
}
public void feed(Iterable events) {
for(ThreadSnapshotEvent e: events) {
if (e.stackTrace() != null && e.stackTrace().depth() > 0) {
String threadName = String.valueOf(e.threadName());
ThreadDump dump = thread(threadName);
int[] trace = intern(e);
dump.count(trace, calc.getWeigth(e));
}
}
}
private int[] intern(ThreadSnapshotEvent e) {
StackFrameList trace = e.stackTrace();
int[] r = new int[trace.depth() + 1];
for(int i = 0; i != trace.depth(); ++i) {
int fp = trace.depth() - 1 - i;
r[i] = intern(formater.toString(trace.frameAt(fp)));
}
if (e.threadState() == null) {
// r = Arrays.copyOf(r, r.length - 1);
r[r.length - 1] = intern("(???)");
}
else {
r[r.length - 1] = intern(e.threadState());
}
return r;
}
ThreadDump thread(String name) {
ThreadDump dump = threads.get(name);
if (dump == null) {
dump = new ThreadDump(name);
threads.put(name, dump);
}
return dump;
}
public void exportJson(StringBuilder sb) {
sb.append("{");
sb.append("frames: ");
exportFrames(sb);
sb.append(", ");
sb.append("frameColors: ");
exportFrameColors(sb);
sb.append(", ");
sb.append("threads: ");
exportThreads(sb);
sb.append("}");
}
private void exportFrames(StringBuilder sb) {
sb.append("[");
for(String frame: frameSet.keySet()) {
sb.append('"').append(frame).append("\", ");
}
sb.setLength(sb.length() - 2);
sb.append("]");
}
private void exportFrameColors(StringBuilder sb) {
sb.append("[");
for(String col: pallete) {
sb.append(col).append(", ");
}
sb.setLength(sb.length() - 2);
sb.append("]");
}
private void exportThreads(StringBuilder sb) {
sb.append("[");
for(ThreadDump td: threads.values()) {
sb.append("{ name: \"").append(escape(td.threadName)).append("\", traces: [");
for(TraceWeight tw: td.traces) {
sb.append("{ trace: ")
.append(Arrays.toString(tw.trace))
.append(", samples: ")
.append(tw.samples)
.append("}, ");
}
sb.setLength(sb.length() - 2);
sb.append("]}, ");
}
sb.setLength(sb.length() - 2);
sb.append("]");
}
private String escape(String threadName) {
if (threadName.indexOf('\\') >= 0) {
threadName = threadName.replace("\\", "\\\\");
}
if (threadName.indexOf('"') >= 0) {
threadName = threadName.replace("\"", "\\\"");
}
return threadName;
}
static class ThreadDump {
public final String threadName;
public final List traces;
public ThreadDump(String name) {
this.threadName = name;
this.traces = new ArrayList();
}
public void count(int[] trace, long weight) {
for(TraceWeight t: traces) {
if (Arrays.equals(t.trace, trace)) {
t.samples += weight;
return;
}
}
TraceWeight tw = new TraceWeight(trace, weight);
traces.add(tw);
}
}
static class TraceWeight {
public final int[] trace;
public long samples;
public TraceWeight(int[] trace, long samples) {
this.trace = trace;
this.samples = samples;
}
}
}