All Downloads are FREE. Search and download functionalities are using the official Maven repository.

js.flamer-graph.js Maven / Gradle / Ivy

There is a newer version: 0.23
Show newest version
(function($, doc, wnd) {    

    function sampleCount(dataSet, prefix) {

        if (prefix === undefined) {
            prefix = [];
        }

        var count = 0;

        for(var i = 0; i < dataSet.threads.length; ++i) {
            var td = dataSet.threads[i];
            for(var j = 0; j < td.traces.length; ++j) {
                if (arrayStartsWith(td.traces[j].trace, prefix)) {
                    count += td.traces[j].samples;
                }
            }
        }

        return count;
    }

    function sampleCountForFrame(dataSet, frame) {

        var count = 0;

        for(var i = 0; i < dataSet.threads.length; ++i) {
            var td = dataSet.threads[i];
            for(var j = 0; j < td.traces.length; ++j) {
                if (td.traces[j].trace.indexOf(frame) >= 0) {
                    count += td.traces[j].samples;
                }
            }
        }

        return count;
    }

        function arrayStartsWith(a, pref) {
        if (a.length < pref.length) {
            return false;
        }
        for(var i = 0; i < pref.length; ++i) {
            if (a[i] != pref[i]) {
                return false;
            }
        }
        return true;
    }

    function toPath(a) {
        var p = "p";
        for(var i = 0; i < a.length; ++i) {
            if (p != "p") {
                p += "_";
            }
            p += a[i];
        }
        return p;
    }

    function toPrefix(a) {
        var f = a.lastIndexOf("_");
        a = a.slice(0, f);
        a = a.slice(a.lastIndexOf("p") + 1);
        var pref = a.split("_");
        for(var i = 0; i < pref.length; ++i) {
            pref[i] = Number(pref[i]);
        }
        return pref;
    }

    function selectByPath(tree, path) {
        var node = tree;
        for(var i = 0; i < path.length; ++i) {
            node = node["f" + path[i]];
            if (node === undefined) {
                return node;
            }
        }
        return node;
    }

    function collectTree(dataSet) {
        var root = {};

        var i;
        for(i = 0; i < dataSet.threads.length; ++i) {
            var j;
            var td = dataSet.threads[i];
            for(j = 0; j < td.traces.length; ++j) {
                var k;
                var t = td.traces[j].trace;
                var node = root;
                for(k = 0; k < t.length; ++k) {
                    var f = "f" +  t[k];
                    if (node[f] === undefined) {
                        node[f] = {};
                    }
                    node = node[f];
                    var pref = t.slice(0, k + 1);
                    node.path = toPath(pref);
                    node.frame = dataSet.frames[t[k]];
                    node.frameNo = t[k];
                    node.samples = sampleCount(dataSet, pref);
                }
            }
        }

        root.samples = sampleCount(dataSet, []);

        return root;
    }
    
    function createInfoElement(ns, treeNode) {
        if (treeNode.frame === undefined) {
            var stub = $("
"); stub.css({display: "none"}); stub.text("no frame"); return stub; } else if (treeNode.frame == "(WAITING)") { var wnode = $("
"); wnode.attr("id", ns + treeNode.path + "_node"); return wnode; } else if (treeNode.frame == "(TIMED_WAITING)") { var twnode = $("
"); twnode.attr("id", ns + treeNode.path + "_node"); return twnode; } else if (treeNode.frame == "(BLOCKED)") { var bnode = $("
"); bnode.attr("id", ns + treeNode.path + "_node"); return bnode; } else if (treeNode.frame == "(RUNNABLE)") { var rnode = $("
"); rnode.attr("id", ns + treeNode.path + "_node"); return rnode; } else if (treeNode.frame == "(IO)") { var ionode = $("
"); ionode.attr("id", ns + treeNode.path + "_node"); return ionode; } else if (treeNode.frame == "(???)") { var tnode = $("
"); tnode.attr("id", ns + treeNode.path + "_node"); return tnode; } else { var fnode = $("
"); fnode.addClass(ns + "fr" + treeNode.frameNo) fnode.attr("id", ns + treeNode.path + "_node"); fnode.text(treeNode.frame); return fnode; } } function createTreeElement(ns, treeNode, weight, threshold) { if (treeNode.samples < threshold) { var stub = $("
"); stub.css({display: "none"}); stub.text("small element stub"); return stub; } else { var div = $("
"); if (treeNode.path !== undefined) { div.attr("id", ns + treeNode.path + "_box"); } div.css({flexBasis: (weight + "%")}); var children = []; for(var prop in treeNode) { if (prop.startsWith("f")) { children[children.length] = treeNode[prop]; } } if (children.length == 1 && children[0].samples == treeNode.samples) { div.append(createTreeElement(ns, children[0], 100, threshold)); } else if (children.length > 0) { children.sort(function(a, b) {a.samples - b.samples}); var row = $("
"); for(var i = 0; i < children.length; ++i) { var cw = 100 * children[i].samples / treeNode.samples; var node = createTreeElement(ns, children[i], cw, threshold); row.append(node); } div.append(row); } var finfo = createInfoElement(ns, treeNode); div.append(finfo); return div; } } function placeHover(container, hover, event) { var hoverx = event.pageX + 10; var hovery = event.pageY + 15; var hw = hover.width(); var hh = hover.height(); var cx = container.position().left; var cw = container.outerWidth(); var cy = container.position().top; var ch = container.outerHeight(); if (hoverx + hw > cx + cw) { if (hw > cw) { hoverx = cx; } else { hoverx = cx + cw - hw; } } if (hovery + hh > cy + ch) { hovery = event.pageY - hh -15; } hover.css({ top: hovery, left: hoverx }); } function fmtPercent(val) { return Number(val * 100).toFixed(2) + "%"; } function toState(frame) { if (frame == "(???)") { return "Terminal"; } else { return frame.slice(1, frame.length - 1); } } function createFrameGraph(host$, flameModel) { var hostId = host$.attr("id"); function updateHoverText(node, prefix, tree, dataSet) { node.empty(); debug("updateHoverText: " + prefix); var fid = prefix[prefix.length - 1]; var fnode = selectByPath(tree, prefix); var frame = fnode.frame; if (frame.startsWith("(")) { // this is terminator frame, display last frame var state = toState(frame); var stCount = fnode.samples; fid = prefix[prefix.length - 2]; fnode = selectByPath(tree, prefix.slice(0, -1)); frame = fnode.frame; } var totalSamples = sampleCount(dataSet, []); var nodeSampleCount = fnode.samples; var globalCount = sampleCountForFrame(dataSet, fid); $('

').text(frame).appendTo(node); if (state !== undefined) { var lbl = state + ": " + stCount + " (" + fmtPercent(stCount / totalSamples) + ")"; $('

').text(lbl).appendTo(node); } $('

').text("Sample count: " + nodeSampleCount + " (" + fmtPercent(nodeSampleCount / totalSamples) + ")").appendTo(node); $('

').text("Global frame frequency: " + globalCount + " (" + fmtPercent(globalCount / totalSamples) + ")").appendTo(node); if (flameModel.filters.zoom) { var unzoomedCount = sampleCountForFrame(flameModel.filteredData, fid); $('

').text("Unzoomed frame count: " + unzoomedCount).appendTo(node); } } function zoomFrame(frame) { if (flameModel.filters.zoom) { flameModel.filters.zoom.push(frame); } else { flameModel.filters.zoom = [-1, frame]; } debug("New zoom path: " + flameModel.filters.zoom); flameModel.update(); } function zoomPath(path) { flameModel.filters.zoom = path; debug("New zoom path: " + path); flameModel.update(); } function unzoom() { flameModel.filters.zoom = null; flameModel.update(); } function createStackTrace(prefix, tree, dataSet) { var box$ = $("

"); var i, fname; var ff = false; if (flameModel.filters.zoom) { var zoom = flameModel.filters.zoom; if (zoom[0] < 0) { for(i = 1; i < zoom.length; ++i) { fname = dataSet.frames[zoom[i]]; $("

").text(fname).prependTo(box$); } ff = true; } else { for(i = 0; i < zoom.length - 1; ++i) { fname = dataSet.frames[zoom[i]]; $("

").text(fname).prependTo(box$); } } } for(i = ff ? 1 : 0; i < prefix.length; ++i) { fname = dataSet.frames[prefix[i]]; $("

").text(fname).prependTo(box$); } return box$; } function updatePopupText(node, prefix, tree, dataSet) { updateHoverText(node, prefix, tree, dataSet); debug("updatePopupText: " + prefix); createStackTrace(prefix, tree, dataSet).appendTo(node); var buttons = $("

"); buttons.appendTo(node); $("").text("zoom by").appendTo(buttons); var zoom = flameModel.filters.zoom; if (!zoom || zoom[0] < 0) { var zoomByFrame = function() { zoomFrame(prefix[prefix.length - 1]); } $("").text("frame").click(zoomByFrame).appendTo(buttons); } if (!zoom || zoom[0] >= 0) { var path = (zoom ? zoom.slice(0, -1) : []).concat(prefix); var zoomByPath = function() { zoomPath(path); } $("").text("trace").click(zoomByPath).appendTo(buttons); } } function updateFramePallete(dataSet) { var pal = ""; for(var i = 0; i < dataSet.frameColors.length; ++i) { if (dataSet.frameColors[i] != null && dataSet.frameColors[i] !== undefined) { pal += "div." + hostId + "_fr" + i + " {background-color: " + dataSet.frameColors[i] + ";}\n"; } } var id = hostId + "_frameColors"; updateStyleSection(id, pal); } function updateStyleSection(id, styleSheet) { var stBlock = $(""); stBlock.text(styleSheet); if ($("#" + id).length == 0) { $("html>head").append(stBlock); } else { $("#" + id).replaceWith(stBlock); } } function installTooltips(tree, dataSet) { host$.unbind("mouseleave"); host$.unbind("mousemove"); host$.unbind("click"); var lastHightlight = null; var lastTooltip = ""; var pinTooltip = false; var hover$ = $("#" + hostId + ">div.flameHover"); hover$.hide(); updateStyleSection(hostId + "_highlight", ""); $("#" + hostId + " .flameNode").mouseleave(function(){ if (!pinTooltip) { $("#" + hostId + ">div.flameHover").hide(); } }); $("#" + hostId + " .flameNode").mousemove( function(e) { if (!pinTooltip) { var node = this; if (node == null) { hover$.hide(); lastTooltip = ""; } else { hover$.show(); if (lastTooltip != node.id) { lastTooltip = node.id; updateHoverText(hover$, toPrefix(node.id), tree, dataSet); } } placeHover($("#" + hostId), hover$, e); } } ); function clickHandler(e) { e.stopImmediatePropagation(); var highlight = null; var path = null; var node = this; var id = node.id; if (id && id.indexOf("_node")>=0) { path = toPrefix(id); node = selectByPath(tree, path); debug("click on " + id); if (node.frame.startsWith("(")) { // status node not clickable } else { debug("click id: " + path); highlight = node.frameNo; } } var showPopup = false; if (highlight && highlight == lastHightlight) { if (hover$.is(":visible")) { highlight = null; } else { showPopup = true; } } lastHightlight = highlight; if (highlight) { debug("highlight: " + highlight); var col = dataSet.frameColors[highlight]; var stl = "" stl += "div." + hostId + "_fr" + highlight + " {"; stl += "border-color: " + col + ";"; stl += "background-color: #FAF;"; stl += "font-weight: bold;"; stl += "}\n"; updateStyleSection(hostId + "_highlight", stl); pinTooltip = true; lastTooltip = ""; if (showPopup) { updatePopupText(hover$, path, tree, dataSet); hover$.show().queue(function() { placeHover($("#" + hostId), hover$, e); hover$.clearQueue(); }); } else { hover$.hide(); } } else { debug("highlight disable"); updateStyleSection(hostId + "_highlight", ""); pinTooltip = false; hover$.hide(); } } function hideHighlight(e) { debug("highlight disable via flame area"); updateStyleSection(hostId + "_highlight", ""); lastHightlight = null; pinTooltip = false; hover$.hide(); } $("#" + hostId + " .flameArea").click(hideHighlight); $("#" + hostId + " .flameNode").click(clickHandler); } function createZoomFrameNode(frame, frameNo) { var fnode = $("
"); fnode.addClass(hostId + "_fr" + frameNo); fnode.text(frame); return fnode; } function createZoomBar() { var stack = $("
"); var bar = $("
"); var zoomInfo, i, fr, frame; if (flameModel.filters.zoom[0] < 0) { var n = flameModel.filters.zoom.length - 1; if (n > 1) { zoomInfo = "zoom by " + n + " frames"; } else { zoomInfo = "zoom by frame"; } for(i = 0; i < n; ++i) { fr = flameModel.filters.zoom[i + 1]; frame = flameModel.data.frames[fr]; createZoomFrameNode(frame, fr).prependTo(stack); } } else { zoomInfo = "zoom by trace"; for(i = 0; i < flameModel.filters.zoom.length - 1; ++i) { fr = flameModel.filters.zoom[i]; frame = flameModel.data.frames[fr]; createZoomFrameNode(frame, fr).prependTo(stack); } } $("

").text(zoomInfo).appendTo(bar); $("

").text("Click to unzoom").appendTo(bar); bar.click(unzoom); bar.prependTo(stack); return stack; } function redrawGraph() { var dataSet = flameModel.zoomedData; var rootNode = $(host$.find("div.flameRoot")); if (!dataSet.threads || dataSet.threads.length == 0) { rootNode.empty(); $("

").text("No data matching").appendTo(rootNode); } else { var ns = hostId + "_"; var totalSamples = sampleCount(dataSet, []); var graphWidth = rootNode.innerWidth(); var tree = collectTree(dataSet); var threshold = 4 * totalSamples / graphWidth; var graph = createTreeElement(ns, tree, 100, threshold); rootNode.empty(); rootNode.append(graph); if (flameModel.filters.zoom) { debug("show zoom bar"); rootNode.append(createZoomBar()); } installTooltips(tree, dataSet); } } // main updateFramePallete(flameModel.data); flameModel.onModelUpdated.redrawGraph = redrawGraph; redrawGraph(); } wnd.initFlameGraph = createFrameGraph; }(jQuery, document, window));




© 2015 - 2024 Weber Informatics LLC | Privacy Policy