org.gridkit.jvmtool.stacktrace.analytics.flame.AbstractFlameCharter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sjk-stacktrace Show documentation
Show all versions of sjk-stacktrace Show documentation
Thread dumps: capture and encoding
/**
* Copyright 2016 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.stacktrace.analytics.flame;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import java.util.SortedMap;
import java.util.TreeMap;
import org.gridkit.jvmtool.stacktrace.GenericStackElement;
import org.gridkit.jvmtool.stacktrace.StackFrame;
import org.gridkit.jvmtool.stacktrace.StackFrameList;
public abstract class AbstractFlameCharter {
private static final GenericStackElement[] ROOT = new GenericStackElement[0];
private static final Locale SVG_LOCALE;
static {
SVG_LOCALE = Locale.ROOT;
}
private Node root;
private FlameColorPicker colorPicker = new DefaultColorPicker();
public AbstractFlameCharter() {
root = new Node(ROOT, comparator());
}
protected abstract Comparator comparator();
public void setColorPicker(FlameColorPicker cp) {
this.colorPicker = cp;
}
public void feed(StackFrameList trace) {
Node node = root;
++node.totalCount;
for(int i = trace.depth(); i > 0; --i) {
StackFrame f = trace.frameAt(i - 1).withoutSource();
Node c = node.child(f);
++c.totalCount;
node = c;
}
++node.terminalCount;
}
public void renderSVG(String title, int width, Writer writer) throws IOException {
int topm = 24;
int bm = 0;
int frameheight = 16;
int threashold = (int)(1.5d * root.totalCount / width);
int maxDepth = calculateMaxDepth(root, threashold);
int height = maxDepth * frameheight + topm + bm;
appendHeader(width, height, writer);
format(writer, "\n", width, height);
format(writer, "%s \n", width/2, topm, title);
appendChildNodes(writer, root, 0, width, height - frameheight, frameheight, threashold);
format(writer, "");
}
private int calculateMaxDepth(Node node, int threshold) {
if (node.totalCount < threshold) {
return 0;
}
else {
int max = 0;
for(Node n: node.children.values()) {
max = Math.max(max, calculateMaxDepth(n, threshold));
}
return max + 1;
}
}
private void appendChildNodes(Writer writer, Node node, int xoffs, int width, int height, int frameheight, int threshold) throws IOException {
int x = xoffs;
// x += node.terminalCount / 2;
for(Node child: node.children.values()) {
if (child.totalCount > threshold) {
renderNode(writer, child, x, height, width, frameheight);
appendChildNodes(writer, child, x, width, height - frameheight, frameheight, threshold);
}
x += child.totalCount;
}
if (node.terminalCount > threshold) {
renderSmoke(writer, x, node.terminalCount, height, width, frameheight);
}
}
private void renderNode(Writer writer, Node node, int x, int height, int width, int frameheight) throws IOException {
double rx = (double)(width) * x / root.totalCount;
double rw = (double)(width) * node.totalCount / root.totalCount;
double ry = height;
double rh = frameheight;
int c = colorPicker.pickColor(node.path);
int cr = (0xFF) & (c >> 16);
int cg = (0xFF) & (c >> 8);
int cb = (0xFF) & (c >> 0);
// Node box
format(writer, "\n");
format(writer, "%s (%d samples, %.2f%%) \n",
escape(describe(node)), node.totalCount, 100d * node.totalCount / root.totalCount);
format(writer, "\n",
rx, ry, rw, rh, cr, cg, cb);
format(writer, "%s \n",
rx + 10, ry + frameheight - 3, escape(trimStr(describe(node), (int)(rw - 10) / 7)));
format(writer, " \n");
// if (node.terminalCount == node.totalCount) {
// renderSmoke(writer, x, node.terminalCount, height, width, frameheight);
// }
}
private void renderSmoke(Writer writer, int x, int samples, int height, int width, int frameheight) throws IOException {
double rx = (double)(width) * x / root.totalCount;
double rw = (double)(width) * samples / root.totalCount;
double ry = height;
double rh = frameheight;
format(writer, "\n");
format(writer, "%d samples, %.2f%% ", samples, 100d * samples / root.totalCount);
format(writer, "\n",
rx, ry + rh / 2, rw, 3f);
format(writer, " \n");
}
private String trimStr(String describe, int len) {
if (len < 3) {
return "";
}
StringBuilder sb = new StringBuilder();
int n = Math.min(describe.length(), len);
boolean trimed = describe.length() > len;
for(int i = 0; i != n; ++i) {
if (trimed && i > n - 3) {
sb.append('.');
}
else {
sb.append(describe.charAt(i));
}
}
return sb.toString();
}
protected String escape(String text) {
return text
.replace((CharSequence)"<", "<")
.replace((CharSequence)">", ">");
}
protected abstract String describe(Node node);
private void format(Writer writer, String format, Object... args) throws IOException {
writer.append(String.format(SVG_LOCALE, format, args));
}
protected void appendHeader(int width, int height, Writer writer) throws IOException {
format(writer, "\n");
format(writer, "