Advertisement
Guest User

flameGraph

a guest
Jan 16th, 2017
144
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. require.config({
  2.     paths : {
  3.         "app" : "../app",
  4.         "d3" : "../app/search/d3.v3.min"
  5.     }
  6. });
  7.  
  8. require([
  9.         "jquery",
  10.         "underscore",
  11.         "splunkjs/mvc",
  12.         "d3",
  13.         "app/search/d3.tip",
  14.         "splunkjs/mvc/simplexml/ready!"
  15.     ],
  16.     function (
  17.         $,
  18.         _,
  19.         mvc,
  20.         d3,
  21.         d3tooltip) {
  22.     var search = splunkjs.mvc.Components.get("columnSearch"); // Get the search manager
  23.     var results = search.data("results"); // Get its results
  24.  
  25.     // A node within the JSON tree with reference to its parents to calculate their value
  26.     function Node(name, parentNode) {
  27.         this.children = [];
  28.         this.name = name;
  29.         this.value = 0;
  30.         this.parentNode = parentNode;
  31.  
  32.         // Adding value to a node also adds this value to its parents
  33.         this.addValue = function (value) {
  34.             this.value += parseInt(value);
  35.             if (this.parentNode != null) {
  36.                 this.parentNode.addValue(value);
  37.             }
  38.         };
  39.  
  40.         // Method to check if node by given name is already in child nodes
  41.         this.hasChildNamed = function (name) {
  42.             for (var child = 0; child < this.children.length; child++) {
  43.                 if (this.children[child].name == name) {
  44.                     return this.children[child];
  45.                 }
  46.             }
  47.             return null;
  48.         };
  49.     };
  50.  
  51.     // As soon as search returns results, parse them
  52.     results.on("data", function () {
  53.         if (!results.hasData())
  54.             return;
  55.         var collection = results.collection().toJSON();
  56.  
  57.         var levels = results.collection().raw.fields; // Assuming first fields are level names,
  58.         var valueLabel = levels.pop(); // last field represents value
  59.  
  60.         var nestedJSON = new Node('root', null); // This contains the object given to the viz at the end
  61.  
  62.         // Go over each result row
  63.         for (var resultIndex = 0; resultIndex < collection.length; resultIndex++) {
  64.             var result = collection[resultIndex];
  65.             var currentNode = nestedJSON;
  66.  
  67.             // And over each column in these results
  68.             for (var levelIndex = 0; levelIndex < levels.length; levelIndex++) {
  69.                 var level = levels[levelIndex];
  70.                 var name = result[levels[levelIndex]];
  71.  
  72.                 // See if current node has a child by the name of the current result, add if not
  73.                 var treePosition = currentNode.hasChildNamed(name);
  74.                 if (treePosition) {
  75.                     currentNode = treePosition;
  76.                 } else {
  77.                     var newNode = new Node(name, currentNode);
  78.                     currentNode.children.push(newNode);
  79.                     currentNode = newNode;
  80.                     if (levelIndex == levels.length - 1 || result[levels[levelIndex + 1]] == "-") {
  81.                         newNode.addValue(result[valueLabel]);
  82.                         break;
  83.                     }
  84.                 }
  85.             }
  86.         }
  87.         // Done parsing, render viz
  88.         d3.select("#flameTarget")
  89.         .datum(nestedJSON)
  90.         .call(flameGraph);
  91.     });
  92.  
  93.     var flameGraph = flameGraph()
  94.         .height(150) // adjust to needs, will clip if too little
  95.         .width(960)
  96.         .cellHeight(20)
  97.         .transitionDuration(750)
  98.         .transitionEase('cubic-in-out')
  99.         .sort(true)
  100.         //Example to sort in reverse order
  101.         //.sort(function(a,b){ return d3.descending(a.name, b.name);})
  102.         .title("");
  103.  
  104.     // Copied Code for flame graph, kept in here for simplicity (should be moved to separate file one day)
  105.     function flameGraph() {
  106.  
  107.         var w = 960, // graph width
  108.         h = 540, // graph height
  109.         c = 18, // cell height
  110.         selection = null, // selection
  111.         tooltip = true, // enable tooltip
  112.         title = "", // graph title
  113.         transitionDuration = 750,
  114.         transitionEase = "cubic-in-out", // tooltip offset
  115.         sort = true,
  116.         reversed = false, // reverse the graph direction
  117.         clickHandler = null;
  118.  
  119.         var tip = d3tooltip()
  120.             .direction("s")
  121.             .offset([8, 0])
  122.             .attr('class', 'd3-flame-graph-tip')
  123.             .html(function (d) {
  124.                 return label(d);
  125.             });
  126.  
  127.         var labelFormat = function (d) {
  128.             return d.name + " (" + d3.round(100 * d.dx, 3) + "%, " + d.value + " samples)";
  129.         };
  130.  
  131.         function setDetails(t) {
  132.             var details = document.getElementById("details");
  133.             if (details)
  134.                 details.innerHTML = t;
  135.         }
  136.  
  137.         function label(d) {
  138.             if (!d.dummy) {
  139.                 return labelFormat(d);
  140.             } else {
  141.                 return "";
  142.             }
  143.         }
  144.  
  145.         function name(d) {
  146.             return d.name;
  147.         }
  148.  
  149.         var colorMapper = function (d) {
  150.             return d.highlight ? "#E600E6" : colorHash(d.name);
  151.         };
  152.  
  153.         function generateHash(name) {
  154.             // Return a vector (0.0->1.0) that is a hash of the input string.
  155.             // The hash is computed to favor early characters over later ones, so
  156.             // that strings with similar starts have similar vectors. Only the first
  157.             // 6 characters are considered.
  158.             var hash = 0,
  159.             weight = 1,
  160.             max_hash = 0,
  161.             mod = 10,
  162.             max_char = 6;
  163.             if (name) {
  164.                 for (var i = 0; i < name.length; i++) {
  165.                     if (i > max_char) {
  166.                         break;
  167.                     }
  168.                     hash += weight * (name.charCodeAt(i) % mod);
  169.                     max_hash += weight * (mod - 1);
  170.                     weight *= 0.70;
  171.                 }
  172.                 if (max_hash > 0) {
  173.                     hash = hash / max_hash;
  174.                 }
  175.             }
  176.             return hash;
  177.         }
  178.  
  179.         function colorHash(name) {
  180.             // Return an rgb() color string that is a hash of the provided name,
  181.             // and with a warm palette.
  182.             var vector = 0;
  183.             if (name) {
  184.                 name = name.replace(/.*`/, ""); // drop module name if present
  185.                 name = name.replace(/\(.*/, ""); // drop extra info
  186.                 vector = generateHash(name);
  187.             }
  188.             var r = 200 + Math.round(55 * vector);
  189.             var g = 0 + Math.round(230 * (1 - vector));
  190.             var b = 0 + Math.round(55 * (1 - vector));
  191.             return "rgb(" + r + "," + g + "," + b + ")";
  192.         }
  193.  
  194.         function augment(data) {
  195.             // Augment partitioning layout with "dummy" nodes so that internal nodes'
  196.             // values dictate their width. Annoying, but seems to be least painful
  197.             // option.  https://github.com/mbostock/d3/pull/574
  198.             if (data.children && (data.children.length > 0)) {
  199.                 data.children.forEach(augment);
  200.                 var childValues = 0;
  201.                 data.children.forEach(function (child) {
  202.                     childValues += child.value;
  203.                 });
  204.                 if (childValues < data.value) {
  205.                     data.children.push({
  206.                         "name" : "",
  207.                         "value" : data.value - childValues,
  208.                         "dummy" : true
  209.                     });
  210.                 }
  211.             }
  212.         }
  213.  
  214.         function hide(d) {
  215.             if (!d.original) {
  216.                 d.original = d.value;
  217.             }
  218.             d.value = 0;
  219.             if (d.children) {
  220.                 d.children.forEach(hide);
  221.             }
  222.         }
  223.  
  224.         function show(d) {
  225.             d.fade = false;
  226.             if (d.original) {
  227.                 d.value = d.original;
  228.             }
  229.             if (d.children) {
  230.                 d.children.forEach(show);
  231.             }
  232.         }
  233.  
  234.         function getSiblings(d) {
  235.             var siblings = [];
  236.             if (d.parent) {
  237.                 var me = d.parent.children.indexOf(d);
  238.                 siblings = d.parent.children.slice(0);
  239.                 siblings.splice(me, 1);
  240.             }
  241.             return siblings;
  242.         }
  243.  
  244.         function hideSiblings(d) {
  245.             var siblings = getSiblings(d);
  246.             siblings.forEach(function (s) {
  247.                 hide(s);
  248.             });
  249.             if (d.parent) {
  250.                 hideSiblings(d.parent);
  251.             }
  252.         }
  253.  
  254.         function fadeAncestors(d) {
  255.             if (d.parent) {
  256.                 d.parent.fade = true;
  257.                 fadeAncestors(d.parent);
  258.             }
  259.         }
  260.  
  261.         function getRoot(d) {
  262.             if (d.parent) {
  263.                 return getRoot(d.parent);
  264.             }
  265.             return d;
  266.         }
  267.  
  268.         function zoom(d) {
  269.             tip.hide(d);
  270.             hideSiblings(d);
  271.             show(d);
  272.             fadeAncestors(d);
  273.             update();
  274.             if (typeof clickHandler === 'function') {
  275.                 clickHandler(d);
  276.             }
  277.         }
  278.  
  279.         function searchTree(d, term) {
  280.             var re = new RegExp(term),
  281.             searchResults = [];
  282.  
  283.             function searchInner(d) {
  284.                 var label = d.name;
  285.  
  286.                 if (d.children) {
  287.                     d.children.forEach(function (child) {
  288.                         searchInner(child);
  289.                     });
  290.                 }
  291.  
  292.                 if (label.match(re)) {
  293.                     d.highlight = true;
  294.                     searchResults.push(d);
  295.                 } else {
  296.                     d.highlight = false;
  297.                 }
  298.             }
  299.  
  300.             searchInner(d);
  301.             return searchResults;
  302.         }
  303.  
  304.         function clear(d) {
  305.             d.highlight = false;
  306.             if (d.children) {
  307.                 d.children.forEach(function (child) {
  308.                     clear(child);
  309.                 });
  310.             }
  311.         }
  312.  
  313.         function doSort(a, b) {
  314.             if (typeof sort === 'function') {
  315.                 return sort(a, b);
  316.             } else if (sort) {
  317.                 return d3.ascending(a.name, b.name);
  318.             } else {
  319.                 return 0;
  320.             }
  321.         }
  322.  
  323.         var partition = d3.layout.partition()
  324.             .sort(doSort)
  325.             .value(function (d) {
  326.                 return d.v || d.value;
  327.             })
  328.             .children(function (d) {
  329.                 return d.c || d.children;
  330.             });
  331.  
  332.         function update() {
  333.  
  334.             selection.each(function (data) {
  335.  
  336.                 var x = d3.scale.linear().range([0, w]),
  337.                 y = d3.scale.linear().range([0, c]);
  338.  
  339.                 var nodes = partition(data);
  340.  
  341.                 var kx = w / data.dx;
  342.  
  343.                 var g = d3.select(this).select("svg").selectAll("g").data(nodes);
  344.  
  345.                 g.transition()
  346.                 .duration(transitionDuration)
  347.                 .ease(transitionEase)
  348.                 .attr("transform", function (d) {
  349.                     return "translate(" + x(d.x) + ","
  350.                      + (reversed ? y(d.depth) : (h - y(d.depth) - c)) + ")";
  351.                 });
  352.  
  353.                 g.select("rect").transition()
  354.                 .duration(transitionDuration)
  355.                 .ease(transitionEase)
  356.                 .attr("width", function (d) {
  357.                     return d.dx * kx;
  358.                 });
  359.  
  360.                 var node = g.enter()
  361.                     .append("svg:g")
  362.                     .attr("transform", function (d) {
  363.                         return "translate(" + x(d.x) + ","
  364.                          + (reversed ? y(d.depth) : (h - y(d.depth) - c)) + ")";
  365.                     });
  366.  
  367.                 node.append("svg:rect")
  368.                 .attr("width", function (d) {
  369.                     return d.dx * kx;
  370.                 });
  371.  
  372.                 if (!tooltip)
  373.                     node.append("svg:title");
  374.  
  375.                 node.append("foreignObject")
  376.                 .append("xhtml:div");
  377.  
  378.                 g.attr("width", function (d) {
  379.                     return d.dx * kx;
  380.                 })
  381.                 .attr("height", function (d) {
  382.                     return c;
  383.                 })
  384.                 .attr("name", function (d) {
  385.                     return d.name;
  386.                 })
  387.                 .attr("class", function (d) {
  388.                     return d.fade ? "frame fade" : "frame";
  389.                 });
  390.  
  391.                 g.select("rect")
  392.                 .attr("height", function (d) {
  393.                     return c;
  394.                 })
  395.                 .attr("fill", function (d) {
  396.                     return colorMapper(d);
  397.                 })
  398.                 .style("visibility", function (d) {
  399.                     return d.dummy ? "hidden" : "visible";
  400.                 });
  401.  
  402.                 if (!tooltip)
  403.                     g.select("title")
  404.                     .text(label);
  405.  
  406.                 g.select("foreignObject")
  407.                 .attr("width", function (d) {
  408.                     return d.dx * kx;
  409.                 })
  410.                 .attr("height", function (d) {
  411.                     return c;
  412.                 })
  413.                 .select("div")
  414.                 .attr("class", "flamegraph-label")
  415.                 .style("display", function (d) {
  416.                     return (d.dx * kx < 35) || d.dummy ? "none" : "block";
  417.                 })
  418.                 .text(name);
  419.  
  420.                 g.on('click', zoom);
  421.  
  422.                 g.exit().remove();
  423.  
  424.                 g.on('mouseover', function (d) {
  425.                     if (!d.dummy) {
  426.                         if (tooltip)
  427.                             tip.show(d);
  428.                         setDetails(label(d));
  429.                     }
  430.                 }).on('mouseout', function (d) {
  431.                     if (!d.dummy) {
  432.                         if (tooltip)
  433.                             tip.hide(d);
  434.                         setDetails("");
  435.                     }
  436.                 });
  437.             });
  438.         }
  439.  
  440.         function merge(data, samples) {
  441.             samples.forEach(function (sample) {
  442.                 var node = _.find(data, function (element) {
  443.                         return element.name === sample.name;
  444.                     });
  445.  
  446.                 if (node) {
  447.                     node.value += sample.value;
  448.                     if (sample.children) {
  449.                         if (!node.children) {
  450.                             node.children = [];
  451.                         }
  452.                         merge(node.children, sample.children)
  453.                     }
  454.                 } else {
  455.                     data.push(sample);
  456.                 }
  457.             });
  458.         }
  459.  
  460.         function chart(s) {
  461.  
  462.             selection = s;
  463.  
  464.             if (!arguments.length)
  465.                 return chart;
  466.  
  467.             selection.each(function (data) {
  468.  
  469.                 var svg = d3.select(this)
  470.                     .append("svg:svg")
  471.                     .attr("width", w)
  472.                     .attr("height", h)
  473.                     .attr("class", "partition d3-flame-graph")
  474.                     .call(tip);
  475.  
  476.                 svg.append("svg:text")
  477.                 .attr("class", "title")
  478.                 .attr("text-anchor", "middle")
  479.                 .attr("y", "25")
  480.                 .attr("x", w / 2)
  481.                 .attr("fill", "#808080")
  482.                 .text(title);
  483.  
  484.                 augment(data);
  485.  
  486.                 // "creative" fix for node ordering when partition is called for the first time
  487.                 partition(data);
  488.  
  489.             });
  490.  
  491.             // first draw
  492.             update();
  493.         }
  494.  
  495.         chart.height = function (_) {
  496.             if (!arguments.length) {
  497.                 return h;
  498.             }
  499.             h = _;
  500.             return chart;
  501.         };
  502.  
  503.         chart.width = function (_) {
  504.             if (!arguments.length) {
  505.                 return w;
  506.             }
  507.             w = _;
  508.             return chart;
  509.         };
  510.  
  511.         chart.cellHeight = function (_) {
  512.             if (!arguments.length) {
  513.                 return c;
  514.             }
  515.             c = _;
  516.             return chart;
  517.         };
  518.  
  519.         chart.tooltip = function (_) {
  520.             if (!arguments.length) {
  521.                 return tooltip;
  522.             }
  523.             if (typeof _ === "function") {
  524.                 tip = _;
  525.             }
  526.             tooltip = true;
  527.             return chart;
  528.         };
  529.  
  530.         chart.title = function (_) {
  531.             if (!arguments.length) {
  532.                 return title;
  533.             }
  534.             title = _;
  535.             return chart;
  536.         };
  537.  
  538.         chart.transitionDuration = function (_) {
  539.             if (!arguments.length) {
  540.                 return transitionDuration;
  541.             }
  542.             transitionDuration = _;
  543.             return chart;
  544.         };
  545.  
  546.         chart.transitionEase = function (_) {
  547.             if (!arguments.length) {
  548.                 return transitionEase;
  549.             }
  550.             transitionEase = _;
  551.             return chart;
  552.         };
  553.  
  554.         chart.sort = function (_) {
  555.             if (!arguments.length) {
  556.                 return sort;
  557.             }
  558.             sort = _;
  559.             return chart;
  560.         };
  561.  
  562.         chart.reversed = function (_) {
  563.             if (!arguments.length) {
  564.                 return reversed;
  565.             }
  566.             reversed = _;
  567.             return chart;
  568.         };
  569.  
  570.         chart.label = function (_) {
  571.             if (!arguments.length) {
  572.                 return labelFormat;
  573.             }
  574.             labelFormat = _;
  575.             return chart;
  576.         };
  577.  
  578.         chart.search = function (term) {
  579.             var searchResults = [];
  580.             selection.each(function (data) {
  581.                 searchResults = searchTree(data, term);
  582.                 update();
  583.             });
  584.             return searchResults;
  585.         };
  586.  
  587.         chart.clear = function () {
  588.             selection.each(function (data) {
  589.                 clear(data);
  590.                 update();
  591.             });
  592.         };
  593.  
  594.         chart.zoomTo = function (d) {
  595.             zoom(d);
  596.         };
  597.  
  598.         chart.resetZoom = function () {
  599.             selection.each(function (data) {
  600.                 zoom(data); // zoom to root
  601.             });
  602.         };
  603.  
  604.         chart.onClick = function (_) {
  605.             if (!arguments.length) {
  606.                 return clickHandler;
  607.             }
  608.             clickHandler = _;
  609.             return chart;
  610.         };
  611.  
  612.         chart.merge = function (samples) {
  613.             selection.each(function (data) {
  614.                 merge([data], [samples]);
  615.                 augment(data);
  616.             });
  617.             update();
  618.         }
  619.  
  620.         chart.color = function (_) {
  621.             if (!arguments.length) {
  622.                 return colorMapper;
  623.             }
  624.             colorMapper = _;
  625.             return chart;
  626.         };
  627.  
  628.         return chart;
  629.     }
  630. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement