Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6549: Color flame chart based on package name #29

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, Datadog, Inc. All rights reserved.
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2020, Datadog, Inc. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -39,11 +39,13 @@
public class TraceNode {
private final int value;
private final String name;
private final String packageName;
private final List<TraceNode> children = new ArrayList<>();

public TraceNode(String name, int value) {
public TraceNode(String name, int value, String packageName) {
this.name = name;
this.value = value;
this.packageName = packageName;
}

public int getValue() {
@@ -54,6 +56,10 @@ public String getName() {
return name;
}

public String getPackageName() {
return packageName;
}

public List<TraceNode> getChildren() {
return children;
}
@@ -68,6 +74,7 @@ public int hashCode() {
int result = 1;
result = prime * result + ((children == null) ? 0 : children.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((packageName == null) ? 0 : packageName.hashCode());
result = prime * result + value;
return result;
}
@@ -91,12 +98,19 @@ public boolean equals(Object obj) {
return false;
} else if (!name.equals(other.name))
return false;
if (packageName == null) {
if (other.packageName != null) {
return false;
}
} else if (!packageName.equals(other.packageName))
return false;
if (value != other.value)
return false;
return true;
}

public String toString() {
return "TraceNode [name: " + name + ", value: " + value + ", children: " + children.size() + "]";
return "TraceNode [name: " + name + ", value: " + value + ", packageName: " + packageName + ", children: "
+ children.size() + "]";
}
}
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, Datadog, Inc. All rights reserved.
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2020, Datadog, Inc. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -46,6 +46,7 @@

public class TraceTreeUtils {
public final static String DEFAULT_ROOT_NAME = "__root";
public final static String DEFAULT_ROOT_PACKAGE_NAME = "";
public final static FrameSeparator DEFAULT_FRAME_SEPARATOR = new FrameSeparator(FrameCategorization.METHOD, false);

/**
@@ -57,7 +58,8 @@
*/
public static TraceNode createTree(StacktraceModel model, String rootName) {
Fork rootFork = model.getRootFork();
TraceNode root = new TraceNode(rootName == null ? DEFAULT_ROOT_NAME : rootName, rootFork.getItemsInFork());
TraceNode root = new TraceNode(rootName == null ? DEFAULT_ROOT_NAME : rootName, rootFork.getItemsInFork(),
DEFAULT_ROOT_PACKAGE_NAME);
for (Branch branch : rootFork.getBranches()) {
addBranch(root, branch);
}
@@ -78,10 +80,11 @@ public static TraceNode createTree(

private static void addBranch(TraceNode root, Branch branch) {
StacktraceFrame firstFrame = branch.getFirstFrame();
TraceNode currentNode = new TraceNode(format(firstFrame), firstFrame.getItemCount());
TraceNode currentNode = new TraceNode(format(firstFrame), firstFrame.getItemCount(),
formatPackageName(firstFrame));
root.addChild(currentNode);
for (StacktraceFrame frame : branch.getTailFrames()) {
TraceNode newNode = new TraceNode(format(frame), frame.getItemCount());
TraceNode newNode = new TraceNode(format(frame), frame.getItemCount(), formatPackageName(frame));
currentNode.addChild(newNode);
currentNode = newNode;
}
@@ -100,6 +103,12 @@ private static String format(StacktraceFrame sFrame) {
return FormatToolkit.getHumanReadable(method, false, false, true, false, true, false);
}

private static String formatPackageName(StacktraceFrame sFrame) {
IMCFrame frame = sFrame.getFrame();
IMCMethod method = frame.getMethod();
return FormatToolkit.getPackage(method.getType().getPackage());
}

public static String printTree(TraceNode node) {
StringBuilder builder = new StringBuilder();
builder.append("=== Tree Printout ===");
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, Datadog, Inc. All rights reserved.
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2020, Datadog, Inc. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -34,7 +34,6 @@
package org.openjdk.jmc.flightrecorder.flameview.views;

import java.io.IOException;

import java.text.MessageFormat;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
@@ -93,13 +92,15 @@
String jsD3Tip = "jslibs/d3-tip.min.js";
// from: https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@2.0.3/dist/d3-flamegraph.min.js
String jsD3FlameGraph = "jslibs/d3-flamegraph.min.js";
// jmc flameview coloring functions
String jsFlameviewColoring = "jsjmclibs/flameviewColoring.js";

String jsIeLibraries = loadLibraries(jsHtml5shiv, jsRespond);
String jsD3Libraries = loadLibraries(jsD3V4, jsD3Tip, jsD3FlameGraph);

// formatter arguments for the template: %1 - CSSs, %2 - IE9 specific scripts, %3 - 3rd party scripts
// formatter arguments for the template: %1 - CSSs, %2 - IE9 specific scripts, %3 - 3rd party scripts %4 - 4th Flameview Coloring
HTML_PAGE = String.format(fileContent("page.template"), fileContent(cssD3Flamegraph), jsIeLibraries,
jsD3Libraries);
jsD3Libraries, fileContent(jsFlameviewColoring));
}

private static final ExecutorService MODEL_EXECUTOR = Executors.newFixedThreadPool(1);
@@ -252,8 +253,8 @@ private static String render(TraceNode root) {
}

private static void render(StringBuilder builder, TraceNode node) {
String start = String.format("{%s,%s, \"c\": [ ", toJSonKeyValue("n", node.getName()),
toJSonKeyValue("v", String.valueOf(node.getValue())));
String start = String.format("{%s,%s,%s, \"c\": [ ", toJSonKeyValue("n", node.getName()),
toJSonKeyValue("p", node.getPackageName()), toJSonKeyValue("v", String.valueOf(node.getValue())));
builder.append(start);
for (int i = 0; i < node.getChildren().size(); i++) {
render(builder, node.getChildren().get(i));
@@ -0,0 +1,124 @@
/*
Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2020, Datadog, Inc. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
The contents of this file are subject to the terms of either the Universal Permissive License
v 1.0 as shown at http://oss.oracle.com/licenses/upl
or the following license:
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions
and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials provided with
the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to
endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

String.prototype.hashCode = function () {
var hash = 0;
if (this.length === 0) return hash;
for (var i = 0; i < this.length; i++) {
var char = this.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return hash;
};

const rootPackageColor = "darkred";
const invalidPackageColor = "snow";
const packageJavaColorLightGray = "lightgray";
const packageComSunAndJdkColorDarkGray = "darkgray";
const packageSunDarkColorGray = "gray";
const packageRestValueHMax = 360;
const packageRestValueH = 0;
const packageRestSLValues = [42, 53];
const packageConsideredDepth = 3;
const packageMarkerJava = "java";
const packageMarkerSun = "sun";
const packageMarkerComSunAndJdk = "comSunAndJdk";
const packageMarkerRest = "rest";
const packagesIdentifierMap = new Map().set("java.", packageMarkerJava).set("sun.", packageMarkerSun)
.set("com.sun.", packageMarkerComSunAndJdk).set("jdk.", packageMarkerComSunAndJdk);
const packageColorMap = new Map().set("", rootPackageColor);

const colorByPackage = function (p) {
if (p === undefined) {
return invalidPackageColor;
} else {
const packageNameStrip = stripPackageName(p);
const packageSelectedColor = packageColorMap.get(packageNameStrip);
if (packageSelectedColor === undefined) {
const packageMarkerSelected = getPackageMarker(packageNameStrip);
const packageNameStripHash = packageNameStrip.hashCode();
switch (packageMarkerSelected) {
case packageMarkerJava:
packageColorMap.set(packageNameStrip, packageJavaColorLightGray);
break;
case packageMarkerComSunAndJdk:
packageColorMap.set(packageNameStrip, packageComSunAndJdkColorDarkGray);
break;
case packageMarkerSun:
packageColorMap.set(packageNameStrip, packageSunDarkColorGray);
break;
case packageMarkerRest:
const packageRestSelectedColor = createHslColorString(adjustHslPropertyByHash(packageNameStripHash, packageRestValueH, packageRestValueHMax), packageRestSLValues[0], packageRestSLValues[1]);
packageColorMap.set(packageNameStrip, packageRestSelectedColor);
break;
}
return packageColorMap.get(packageNameStrip);
} else {
return packageSelectedColor;
}
}
};

const getPackageMarker = function(p){
for(let k of packagesIdentifierMap.keys()){
if(p.startsWith(k)){
return packagesIdentifierMap.get(k);
}
}
return packageMarkerRest;
};

const stripPackageName = function (p) {
const splitString = p.split("\u002E");
const number = Math.min(splitString.length, packageConsideredDepth);
return splitString.slice(0, number).join("\u002E");
};

const adjustHslPropertyByHash = function (hash, min, max) {
const proposedValue = hash % (max - min) + min;
return Math.min(proposedValue, max);
};

const createHslColorString = function(h,s,l){
return "hsl\u0028" + h + "\u002c " + s + "\u0025\u002c " + l + "\u0025\u0029";
};

const colorCell = function (d) {
return colorByPackage(d.data.p);
};

const adjustTip = function (d) {
return d.data.n + "\u003Cbr\u002F\u003Epackage: " + d.data.p + "\u003Cbr\u002F\u003Esamples: " + d.data.v;
};
This conversation was marked as resolved by mirage22

This comment has been minimized.

Copy link
@thegreystone

thegreystone Jan 13, 2020

Collaborator

Perhaps add newline here.

@@ -13,18 +13,30 @@
<body onresize="resizeFlameGraph()">
<div id="chart"></div>
<script type="text/javascript">%3$s</script>
<script type="text/javascript">%4$s</script>
<script type="text/javascript">

var flameGraph;
var currentJson;

const tip = d3.tip()
.direction("s")
.offset([8, 0])
.attr('class', 'd3-flame-graph-tip')
.html(adjustTip);

function processGraph(jsonObj) {
flameGraph = d3.flamegraph()
.width(windowSize() * 0.9)
.cellHeight(18)
.transitionDuration(500)
.minFrameSize(5)
.transitionEase(d3.easeCubic)
.sort(true)
.title("");
.title("")
.differential(false)
.tooltip(tip)
.color(colorCell);
currentJson = jsonObj;
d3.select("#chart")
.datum(currentJson)
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.