diff options
| -rw-r--r-- | Makefile | 8 | ||||
| -rw-r--r-- | README.md | 8 | ||||
| -rw-r--r-- | assets/ior-by-count-flamegraph.svg | 7591 |
3 files changed, 486 insertions, 7121 deletions
@@ -36,16 +36,16 @@ world: clean generate all .PHONY: flames flames: - perl ~/git/FlameGraph/flamegraph.pl ior-by-path-count-flamegraph.collapsed \ + inferno-flamegraph < ior-by-path-count-flamegraph.collapsed \ --title "I/O Syscall Count" --nametype Path --hash \ > ior-by-path-count-flamegraph.svg; \ - perl ~/git/FlameGraph/flamegraph.pl ior-by-path-duration-flamegraph.collapsed \ + inferno-flamegraph < ior-by-path-duration-flamegraph.collapsed \ --title "I/O Syscall Durations" --nametype Path --hash --countname Nanoseconds \ > ior-by-path-duration-flamegraph.svg; \ - perl ~/git/FlameGraph/flamegraph.pl ior-by-syscall-count-flamegraph.collapsed \ + inferno-flamegraph < ior-by-syscall-count-flamegraph.collapsed \ --title "I/O Syscall Count" --nametype Path --hash \ > ior-by-syscall-count-flamegraph.svg; \ - perl ~/git/FlameGraph/flamegraph.pl ior-by-syscall-duration-flamegraph.collapsed \ + inferno-flamegraph < ior-by-syscall-duration-flamegraph.collapsed \ --title "I/O Syscall Durations" --nametype Path --hash --countname Nanoseconds \ > ior-by-syscall-duration-flamegraph.svg; \ @@ -42,3 +42,11 @@ make sudo cp -v ./libelf/libelf.a /usr/lib64/ ``` +## Inferno Flamegraphs + +We are using Inferno Flamegraphs: https://github.com/jonhoo/inferno + +```sh +cargo install inferno +``` + diff --git a/assets/ior-by-count-flamegraph.svg b/assets/ior-by-count-flamegraph.svg index a565939..d5b4d05 100644 --- a/assets/ior-by-count-flamegraph.svg +++ b/assets/ior-by-count-flamegraph.svg @@ -1,7134 +1,491 @@ -<?xml version="1.0" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" width="1200" height="262" onload="init(evt)" viewBox="0 0 1200 262" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> -<!-- Flame graph stack visualization. See https://github.com/brendangregg/FlameGraph for latest version, and http://www.brendangregg.com/flamegraphs.html for examples. --> -<!-- NOTES: --> -<defs> - <linearGradient id="background" y1="0" y2="1" x1="0" x2="0" > - <stop stop-color="#eeeeee" offset="5%" /> - <stop stop-color="#eeeeb0" offset="95%" /> - </linearGradient> -</defs> -<style type="text/css"> - text { font-family:Verdana; font-size:12px; fill:rgb(0,0,0); } - #search, #ignorecase { opacity:0.1; cursor:pointer; } - #search:hover, #search.show, #ignorecase:hover, #ignorecase.show { opacity:1; } - #subtitle { text-anchor:middle; font-color:rgb(160,160,160); } - #title { text-anchor:middle; font-size:17px} - #unzoom { cursor:pointer; } - #frames > *:hover { stroke:black; stroke-width:0.5; cursor:pointer; } - .hide { display:none; } - .parent { opacity:0.5; } -</style> -<script type="text/ecmascript"> -<![CDATA[ - "use strict"; - var details, searchbtn, unzoombtn, matchedtxt, svg, searching, currentSearchTerm, ignorecase, ignorecaseBtn; - function init(evt) { - details = document.getElementById("details").firstChild; - searchbtn = document.getElementById("search"); - ignorecaseBtn = document.getElementById("ignorecase"); - unzoombtn = document.getElementById("unzoom"); - matchedtxt = document.getElementById("matched"); - svg = document.getElementsByTagName("svg")[0]; - searching = 0; - currentSearchTerm = null; +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" width="1200" height="342" onload="init(evt)" viewBox="0 0 1200 342" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fg="http://github.com/jonhoo/inferno"><!--Flame graph stack visualization. See https://github.com/brendangregg/FlameGraph for latest version, and http://www.brendangregg.com/flamegraphs.html for examples.--><!--NOTES: --><defs><linearGradient id="background" y1="0" y2="1" x1="0" x2="0"><stop stop-color="#eeeeee" offset="5%"/><stop stop-color="#eeeeb0" offset="95%"/></linearGradient></defs><style type="text/css"> +text { font-family:monospace; font-size:12px } +#title { text-anchor:middle; font-size:17px; } +#matched { text-anchor:end; } +#search { text-anchor:end; opacity:0.1; cursor:pointer; } +#search:hover, #search.show { opacity:1; } +#subtitle { text-anchor:middle; font-color:rgb(160,160,160); } +#unzoom { cursor:pointer; } +#frames > *:hover { stroke:black; stroke-width:0.5; cursor:pointer; } +.hide { display:none; } +.parent { opacity:0.5; } +</style><script type="text/ecmascript"><![CDATA[ + var nametype = 'Path'; + var fontsize = 12; + var fontwidth = 0.59; + var xpad = 10; + var inverted = false; + var searchcolor = 'rgb(230,0,230)'; + var fluiddrawing = true; + var truncate_text_right = false; + ]]><![CDATA["use strict"; +var details, searchbtn, unzoombtn, matchedtxt, svg, searching, frames, known_font_width; +function init(evt) { + details = document.getElementById("details").firstChild; + searchbtn = document.getElementById("search"); + unzoombtn = document.getElementById("unzoom"); + matchedtxt = document.getElementById("matched"); + svg = document.getElementsByTagName("svg")[0]; + frames = document.getElementById("frames"); + known_font_width = get_monospace_width(frames); + total_samples = parseInt(frames.attributes.total_samples.value); + searching = 0; - // use GET parameters to restore a flamegraphs state. - var params = get_params(); - if (params.x && params.y) - zoom(find_group(document.querySelector('[x="' + params.x + '"][y="' + params.y + '"]'))); - if (params.s) search(params.s); - } + // Use GET parameters to restore a flamegraph's state. + var restore_state = function() { + var params = get_params(); + if (params.x && params.y) + zoom(find_group(document.querySelector('[*|x="' + params.x + '"][y="' + params.y + '"]'))); + if (params.s) + search(params.s); + }; - // event listeners - window.addEventListener("click", function(e) { - var target = find_group(e.target); - if (target) { - if (target.nodeName == "a") { - if (e.ctrlKey === false) return; - e.preventDefault(); - } - if (target.classList.contains("parent")) unzoom(true); - zoom(target); - if (!document.querySelector('.parent')) { - // we have basically done a clearzoom so clear the url - var params = get_params(); - if (params.x) delete params.x; - if (params.y) delete params.y; - history.replaceState(null, null, parse_params(params)); - unzoombtn.classList.add("hide"); - return; - } + if (fluiddrawing) { + // Make width dynamic so the SVG fits its parent's width. + svg.removeAttribute("width"); + // Edge requires us to have a viewBox that gets updated with size changes. + var isEdge = /Edge\/\d./i.test(navigator.userAgent); + if (!isEdge) { + svg.removeAttribute("viewBox"); + } + var update_for_width_change = function() { + if (isEdge) { + svg.attributes.viewBox.value = "0 0 " + svg.width.baseVal.value + " " + svg.height.baseVal.value; + } - // set parameters for zoom state - var el = target.querySelector("rect"); - if (el && el.attributes && el.attributes.y && el.attributes._orig_x) { - var params = get_params() - params.x = el.attributes._orig_x.value; - params.y = el.attributes.y.value; - history.replaceState(null, null, parse_params(params)); - } - } - else if (e.target.id == "unzoom") clearzoom(); - else if (e.target.id == "search") search_prompt(); - else if (e.target.id == "ignorecase") toggle_ignorecase(); - }, false) + // Keep consistent padding on left and right of frames container. + frames.attributes.width.value = svg.width.baseVal.value - xpad * 2; - // mouse-over for info - // show - window.addEventListener("mouseover", function(e) { - var target = find_group(e.target); - if (target) details.nodeValue = "Path " + g_to_text(target); - }, false) + // Text truncation needs to be adjusted for the current width. + update_text_for_elements(frames.children); - // clear - window.addEventListener("mouseout", function(e) { - var target = find_group(e.target); - if (target) details.nodeValue = ' '; - }, false) + // Keep search elements at a fixed distance from right edge. + var svgWidth = svg.width.baseVal.value; + searchbtn.attributes.x.value = svgWidth - xpad; + matchedtxt.attributes.x.value = svgWidth - xpad; + }; + window.addEventListener('resize', function() { + update_for_width_change(); + }); + // This needs to be done asynchronously for Safari to work. + setTimeout(function() { + unzoom(); + update_for_width_change(); + restore_state(); + }, 0); + } else { + restore_state(); + } +} +// event listeners +window.addEventListener("click", function(e) { + var target = find_group(e.target); + if (target) { + if (target.nodeName == "a") { + if (e.ctrlKey === false) return; + e.preventDefault(); + } + if (target.classList.contains("parent")) unzoom(); + zoom(target); - // ctrl-F for search - // ctrl-I to toggle case-sensitive search - window.addEventListener("keydown",function (e) { - if (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70)) { - e.preventDefault(); - search_prompt(); - } - else if (e.ctrlKey && e.keyCode === 73) { - e.preventDefault(); - toggle_ignorecase(); - } - }, false) + // set parameters for zoom state + var el = target.querySelector("rect"); + if (el && el.attributes && el.attributes.y && el.attributes["fg:x"]) { + var params = get_params() + params.x = el.attributes["fg:x"].value; + params.y = el.attributes.y.value; + history.replaceState(null, null, parse_params(params)); + } + } + else if (e.target.id == "unzoom") { + unzoom(); - // functions - function get_params() { - var params = {}; - var paramsarr = window.location.search.substr(1).split('&'); - for (var i = 0; i < paramsarr.length; ++i) { - var tmp = paramsarr[i].split("="); - if (!tmp[0] || !tmp[1]) continue; - params[tmp[0]] = decodeURIComponent(tmp[1]); - } - return params; - } - function parse_params(params) { - var uri = "?"; - for (var key in params) { - uri += key + '=' + encodeURIComponent(params[key]) + '&'; - } - if (uri.slice(-1) == "&") - uri = uri.substring(0, uri.length - 1); - if (uri == '?') - uri = window.location.href.split('?')[0]; - return uri; - } - function find_child(node, selector) { - var children = node.querySelectorAll(selector); - if (children.length) return children[0]; - } - function find_group(node) { - var parent = node.parentElement; - if (!parent) return; - if (parent.id == "frames") return node; - return find_group(parent); - } - function orig_save(e, attr, val) { - if (e.attributes["_orig_" + attr] != undefined) return; - if (e.attributes[attr] == undefined) return; - if (val == undefined) val = e.attributes[attr].value; - e.setAttribute("_orig_" + attr, val); - } - function orig_load(e, attr) { - if (e.attributes["_orig_"+attr] == undefined) return; - e.attributes[attr].value = e.attributes["_orig_" + attr].value; - e.removeAttribute("_orig_"+attr); - } - function g_to_text(e) { - var text = find_child(e, "title").firstChild.nodeValue; - return (text) - } - function g_to_func(e) { - var func = g_to_text(e); - // if there's any manipulation we want to do to the function - // name before it's searched, do it here before returning. - return (func); - } - function update_text(e) { - var r = find_child(e, "rect"); - var t = find_child(e, "text"); - var w = parseFloat(r.attributes.width.value) -3; - var txt = find_child(e, "title").textContent.replace(/\([^(]*\)$/,""); - t.attributes.x.value = parseFloat(r.attributes.x.value) + 3; + // remove zoom state + var params = get_params(); + if (params.x) delete params.x; + if (params.y) delete params.y; + history.replaceState(null, null, parse_params(params)); + } + else if (e.target.id == "search") search_prompt(); +}, false) +// mouse-over for info +// show +window.addEventListener("mouseover", function(e) { + var target = find_group(e.target); + if (target) details.nodeValue = nametype + " " + g_to_text(target); +}, false) +// clear +window.addEventListener("mouseout", function(e) { + var target = find_group(e.target); + if (target) details.nodeValue = ' '; +}, false) +// ctrl-F for search +window.addEventListener("keydown",function (e) { + if (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70)) { + e.preventDefault(); + search_prompt(); + } +}, false) +// functions +function get_params() { + var params = {}; + var paramsarr = window.location.search.substr(1).split('&'); + for (var i = 0; i < paramsarr.length; ++i) { + var tmp = paramsarr[i].split("="); + if (!tmp[0] || !tmp[1]) continue; + params[tmp[0]] = decodeURIComponent(tmp[1]); + } + return params; +} +function parse_params(params) { + var uri = "?"; + for (var key in params) { + uri += key + '=' + encodeURIComponent(params[key]) + '&'; + } + if (uri.slice(-1) == "&") + uri = uri.substring(0, uri.length - 1); + if (uri == '?') + uri = window.location.href.split('?')[0]; + return uri; +} +function find_child(node, selector) { + var children = node.querySelectorAll(selector); + if (children.length) return children[0]; + return; +} +function find_group(node) { + var parent = node.parentElement; + if (!parent) return; + if (parent.id == "frames") return node; + return find_group(parent); +} +function orig_save(e, attr, val) { + if (e.attributes["fg:orig_" + attr] != undefined) return; + if (e.attributes[attr] == undefined) return; + if (val == undefined) val = e.attributes[attr].value; + e.setAttribute("fg:orig_" + attr, val); +} +function orig_load(e, attr) { + if (e.attributes["fg:orig_"+attr] == undefined) return; + e.attributes[attr].value = e.attributes["fg:orig_" + attr].value; + e.removeAttribute("fg:orig_" + attr); +} +function g_to_text(e) { + var text = find_child(e, "title").firstChild.nodeValue; + return (text) +} +function g_to_func(e) { + var func = g_to_text(e); + // if there's any manipulation we want to do to the function + // name before it's searched, do it here before returning. + return (func); +} +function get_monospace_width(frames) { + // Given the id="frames" element, return the width of text characters if + // this is a monospace font, otherwise return 0. + text = find_child(frames.children[0], "text"); + originalContent = text.textContent; + text.textContent = "!"; + bangWidth = text.getComputedTextLength(); + text.textContent = "W"; + wWidth = text.getComputedTextLength(); + text.textContent = originalContent; + if (bangWidth === wWidth) { + return bangWidth; + } else { + return 0; + } +} +function update_text_for_elements(elements) { + // In order to render quickly in the browser, you want to do one pass of + // reading attributes, and one pass of mutating attributes. See + // https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/ for details. - // Smaller than this size won't fit anything - if (w < 2 * 12 * 0.59) { - t.textContent = ""; - return; - } + // Fall back to inefficient calculation, if we're variable-width font. + // TODO This should be optimized somehow too. + if (known_font_width === 0) { + for (var i = 0; i < elements.length; i++) { + update_text(elements[i]); + } + return; + } - t.textContent = txt; - var sl = t.getSubStringLength(0, txt.length); - // check if only whitespace or if we can fit the entire string into width w - if (/^ *$/.test(txt) || sl < w) - return; + var textElemNewAttributes = []; + for (var i = 0; i < elements.length; i++) { + var e = elements[i]; + var r = find_child(e, "rect"); + var t = find_child(e, "text"); + var w = parseFloat(r.attributes.width.value) * frames.attributes.width.value / 100 - 3; + var txt = find_child(e, "title").textContent.replace(/\([^(]*\)$/,""); + var newX = format_percent((parseFloat(r.attributes.x.value) + (100 * 3 / frames.attributes.width.value))); - // this isn't perfect, but gives a good starting point - // and avoids calling getSubStringLength too often - var start = Math.floor((w/sl) * txt.length); - for (var x = start; x > 0; x = x-2) { - if (t.getSubStringLength(0, x + 2) <= w) { - t.textContent = txt.substring(0, x) + ".."; - return; - } - } - t.textContent = ""; - } + // Smaller than this size won't fit anything + if (w < 2 * known_font_width) { + textElemNewAttributes.push([newX, ""]); + continue; + } - // zoom - function zoom_reset(e) { - if (e.attributes != undefined) { - orig_load(e, "x"); - orig_load(e, "width"); - } - if (e.childNodes == undefined) return; - for (var i = 0, c = e.childNodes; i < c.length; i++) { - zoom_reset(c[i]); - } - } - function zoom_child(e, x, ratio) { - if (e.attributes != undefined) { - if (e.attributes.x != undefined) { - orig_save(e, "x"); - e.attributes.x.value = (parseFloat(e.attributes.x.value) - x - 10) * ratio + 10; - if (e.tagName == "text") - e.attributes.x.value = find_child(e.parentNode, "rect[x]").attributes.x.value + 3; - } - if (e.attributes.width != undefined) { - orig_save(e, "width"); - e.attributes.width.value = parseFloat(e.attributes.width.value) * ratio; - } - } + // Fit in full text width + if (txt.length * known_font_width < w) { + textElemNewAttributes.push([newX, txt]); + continue; + } - if (e.childNodes == undefined) return; - for (var i = 0, c = e.childNodes; i < c.length; i++) { - zoom_child(c[i], x - 10, ratio); - } - } - function zoom_parent(e) { - if (e.attributes) { - if (e.attributes.x != undefined) { - orig_save(e, "x"); - e.attributes.x.value = 10; - } - if (e.attributes.width != undefined) { - orig_save(e, "width"); - e.attributes.width.value = parseInt(svg.width.baseVal.value) - (10 * 2); - } - } - if (e.childNodes == undefined) return; - for (var i = 0, c = e.childNodes; i < c.length; i++) { - zoom_parent(c[i]); - } - } - function zoom(node) { - var attr = find_child(node, "rect").attributes; - var width = parseFloat(attr.width.value); - var xmin = parseFloat(attr.x.value); - var xmax = parseFloat(xmin + width); - var ymin = parseFloat(attr.y.value); - var ratio = (svg.width.baseVal.value - 2 * 10) / width; + var substringLength = Math.floor(w / known_font_width) - 2; + if (truncate_text_right) { + // Truncate the right side of the text. + textElemNewAttributes.push([newX, txt.substring(0, substringLength) + ".."]); + continue; + } else { + // Truncate the left side of the text. + textElemNewAttributes.push([newX, ".." + txt.substring(txt.length - substringLength, txt.length)]); + continue; + } + } - // XXX: Workaround for JavaScript float issues (fix me) - var fudge = 0.0001; + console.assert(textElemNewAttributes.length === elements.length, "Resize failed, please file a bug at https://github.com/jonhoo/inferno/"); - unzoombtn.classList.remove("hide"); + // Now that we know new textContent, set it all in one go so we don't refresh a bazillion times. + for (var i = 0; i < elements.length; i++) { + var e = elements[i]; + var values = textElemNewAttributes[i]; + var t = find_child(e, "text"); + t.attributes.x.value = values[0]; + t.textContent = values[1]; + } +} - var el = document.getElementById("frames").children; - for (var i = 0; i < el.length; i++) { - var e = el[i]; - var a = find_child(e, "rect").attributes; - var ex = parseFloat(a.x.value); - var ew = parseFloat(a.width.value); - var upstack; - // Is it an ancestor - if (0 == 0) { - upstack = parseFloat(a.y.value) > ymin; - } else { - upstack = parseFloat(a.y.value) < ymin; - } - if (upstack) { - // Direct ancestor - if (ex <= xmin && (ex+ew+fudge) >= xmax) { - e.classList.add("parent"); - zoom_parent(e); - update_text(e); - } - // not in current path - else - e.classList.add("hide"); - } - // Children maybe - else { - // no common path - if (ex < xmin || ex + fudge >= xmax) { - e.classList.add("hide"); - } - else { - zoom_child(e, xmin, ratio); - update_text(e); - } - } - } - search(); - } - function unzoom(dont_update_text) { - unzoombtn.classList.add("hide"); - var el = document.getElementById("frames").children; - for(var i = 0; i < el.length; i++) { - el[i].classList.remove("parent"); - el[i].classList.remove("hide"); - zoom_reset(el[i]); - if(!dont_update_text) update_text(el[i]); - } - search(); - } - function clearzoom() { - unzoom(); +function update_text(e) { + var r = find_child(e, "rect"); + var t = find_child(e, "text"); + var w = parseFloat(r.attributes.width.value) * frames.attributes.width.value / 100 - 3; + var txt = find_child(e, "title").textContent.replace(/\([^(]*\)$/,""); + t.attributes.x.value = format_percent((parseFloat(r.attributes.x.value) + (100 * 3 / frames.attributes.width.value))); - // remove zoom state - var params = get_params(); - if (params.x) delete params.x; - if (params.y) delete params.y; - history.replaceState(null, null, parse_params(params)); - } + // Smaller than this size won't fit anything + if (w < 2 * fontsize * fontwidth) { + t.textContent = ""; + return; + } + t.textContent = txt; + // Fit in full text width + if (t.getComputedTextLength() < w) + return; + if (truncate_text_right) { + // Truncate the right side of the text. + for (var x = txt.length - 2; x > 0; x--) { + if (t.getSubStringLength(0, x + 2) <= w) { + t.textContent = txt.substring(0, x) + ".."; + return; + } + } + } else { + // Truncate the left side of the text. + for (var x = 2; x < txt.length; x++) { + if (t.getSubStringLength(x - 2, txt.length) <= w) { + t.textContent = ".." + txt.substring(x, txt.length); + return; + } + } + } + t.textContent = ""; +} +// zoom +function zoom_reset(e) { + if (e.tagName == "rect") { + e.attributes.x.value = format_percent(100 * parseInt(e.attributes["fg:x"].value) / total_samples); + e.attributes.width.value = format_percent(100 * parseInt(e.attributes["fg:w"].value) / total_samples); + } + if (e.childNodes == undefined) return; + for(var i = 0, c = e.childNodes; i < c.length; i++) { + zoom_reset(c[i]); + } +} +function zoom_child(e, x, zoomed_width_samples) { + if (e.tagName == "text") { + var parent_x = parseFloat(find_child(e.parentNode, "rect[x]").attributes.x.value); + e.attributes.x.value = format_percent(parent_x + (100 * 3 / frames.attributes.width.value)); + } else if (e.tagName == "rect") { + e.attributes.x.value = format_percent(100 * (parseInt(e.attributes["fg:x"].value) - x) / zoomed_width_samples); + e.attributes.width.value = format_percent(100 * parseInt(e.attributes["fg:w"].value) / zoomed_width_samples); + } + if (e.childNodes == undefined) return; + for(var i = 0, c = e.childNodes; i < c.length; i++) { + zoom_child(c[i], x, zoomed_width_samples); + } +} +function zoom_parent(e) { + if (e.attributes) { + if (e.attributes.x != undefined) { + e.attributes.x.value = "0.0%"; + } + if (e.attributes.width != undefined) { + e.attributes.width.value = "100.0%"; + } + } + if (e.childNodes == undefined) return; + for(var i = 0, c = e.childNodes; i < c.length; i++) { + zoom_parent(c[i]); + } +} +function zoom(node) { + var attr = find_child(node, "rect").attributes; + var width = parseInt(attr["fg:w"].value); + var xmin = parseInt(attr["fg:x"].value); + var xmax = xmin + width; + var ymin = parseFloat(attr.y.value); + unzoombtn.classList.remove("hide"); + var el = frames.children; + var to_update_text = []; + for (var i = 0; i < el.length; i++) { + var e = el[i]; + var a = find_child(e, "rect").attributes; + var ex = parseInt(a["fg:x"].value); + var ew = parseInt(a["fg:w"].value); + // Is it an ancestor + if (!inverted) { + var upstack = parseFloat(a.y.value) > ymin; + } else { + var upstack = parseFloat(a.y.value) < ymin; + } + if (upstack) { + // Direct ancestor + if (ex <= xmin && (ex+ew) >= xmax) { + e.classList.add("parent"); + zoom_parent(e); + to_update_text.push(e); + } + // not in current path + else + e.classList.add("hide"); + } + // Children maybe + else { + // no common path + if (ex < xmin || ex >= xmax) { + e.classList.add("hide"); + } + else { + zoom_child(e, xmin, width); + to_update_text.push(e); + } + } + } + update_text_for_elements(to_update_text); +} +function unzoom() { + unzoombtn.classList.add("hide"); + var el = frames.children; + for(var i = 0; i < el.length; i++) { + el[i].classList.remove("parent"); + el[i].classList.remove("hide"); + zoom_reset(el[i]); + } + update_text_for_elements(el); +} +// search +function reset_search() { + var el = document.querySelectorAll("#frames rect"); + for (var i = 0; i < el.length; i++) { + orig_load(el[i], "fill") + } + var params = get_params(); + delete params.s; + history.replaceState(null, null, parse_params(params)); +} +function search_prompt() { + if (!searching) { + var term = prompt("Enter a search term (regexp " + + "allowed, eg: ^ext4_)", ""); + if (term != null) { + search(term) + } + } else { + reset_search(); + searching = 0; + searchbtn.classList.remove("show"); + searchbtn.firstChild.nodeValue = "Search" + matchedtxt.classList.add("hide"); + matchedtxt.firstChild.nodeValue = "" + } +} +function search(term) { + var re = new RegExp(term); + var el = frames.children; + var matches = new Object(); + var maxwidth = 0; + for (var i = 0; i < el.length; i++) { + var e = el[i]; + // Skip over frames which are either not visible, or below the zoomed-to frame + if (e.classList.contains("hide") || e.classList.contains("parent")) { + continue; + } + var func = g_to_func(e); + var rect = find_child(e, "rect"); + if (func == null || rect == null) + continue; + // Save max width. Only works as we have a root frame + var w = parseInt(rect.attributes["fg:w"].value); + if (w > maxwidth) + maxwidth = w; + if (func.match(re)) { + // highlight + var x = parseInt(rect.attributes["fg:x"].value); + orig_save(rect, "fill"); + rect.attributes.fill.value = searchcolor; + // remember matches + if (matches[x] == undefined) { + matches[x] = w; + } else { + if (w > matches[x]) { + // overwrite with parent + matches[x] = w; + } + } + searching = 1; + } + } + if (!searching) + return; + var params = get_params(); + params.s = term; + history.replaceState(null, null, parse_params(params)); - // search - function toggle_ignorecase() { - ignorecase = !ignorecase; - if (ignorecase) { - ignorecaseBtn.classList.add("show"); - } else { - ignorecaseBtn.classList.remove("show"); - } - reset_search(); - search(); - } - function reset_search() { - var el = document.querySelectorAll("#frames rect"); - for (var i = 0; i < el.length; i++) { - orig_load(el[i], "fill") - } - var params = get_params(); - delete params.s; - history.replaceState(null, null, parse_params(params)); - } - function search_prompt() { - if (!searching) { - var term = prompt("Enter a search term (regexp " + - "allowed, eg: ^ext4_)" - + (ignorecase ? ", ignoring case" : "") - + "\nPress Ctrl-i to toggle case sensitivity", ""); - if (term != null) search(term); - } else { - reset_search(); - searching = 0; - currentSearchTerm = null; - searchbtn.classList.remove("show"); - searchbtn.firstChild.nodeValue = "Search" - matchedtxt.classList.add("hide"); - matchedtxt.firstChild.nodeValue = "" - } - } - function search(term) { - if (term) currentSearchTerm = term; - if (currentSearchTerm === null) return; - - var re = new RegExp(currentSearchTerm, ignorecase ? 'i' : ''); - var el = document.getElementById("frames").children; - var matches = new Object(); - var maxwidth = 0; - for (var i = 0; i < el.length; i++) { - var e = el[i]; - var func = g_to_func(e); - var rect = find_child(e, "rect"); - if (func == null || rect == null) - continue; - - // Save max width. Only works as we have a root frame - var w = parseFloat(rect.attributes.width.value); - if (w > maxwidth) - maxwidth = w; - - if (func.match(re)) { - // highlight - var x = parseFloat(rect.attributes.x.value); - orig_save(rect, "fill"); - rect.attributes.fill.value = "rgb(230,0,230)"; - - // remember matches - if (matches[x] == undefined) { - matches[x] = w; - } else { - if (w > matches[x]) { - // overwrite with parent - matches[x] = w; - } - } - searching = 1; - } - } - if (!searching) - return; - var params = get_params(); - params.s = currentSearchTerm; - history.replaceState(null, null, parse_params(params)); - - searchbtn.classList.add("show"); - searchbtn.firstChild.nodeValue = "Reset Search"; - - // calculate percent matched, excluding vertical overlap - var count = 0; - var lastx = -1; - var lastw = 0; - var keys = Array(); - for (k in matches) { - if (matches.hasOwnProperty(k)) - keys.push(k); - } - // sort the matched frames by their x location - // ascending, then width descending - keys.sort(function(a, b){ - return a - b; - }); - // Step through frames saving only the biggest bottom-up frames - // thanks to the sort order. This relies on the tree property - // where children are always smaller than their parents. - var fudge = 0.0001; // JavaScript floating point - for (var k in keys) { - var x = parseFloat(keys[k]); - var w = matches[keys[k]]; - if (x >= lastx + lastw - fudge) { - count += w; - lastx = x; - lastw = w; - } - } - // display matched percent - matchedtxt.classList.remove("hide"); - var pct = 100 * count / maxwidth; - if (pct != 100) pct = pct.toFixed(1) - matchedtxt.firstChild.nodeValue = "Matched: " + pct + "%"; - } -]]> -</script> -<rect x="0.0" y="0" width="1200.0" height="262.0" fill="url(#background)" /> -<text id="title" x="600.00" y="24" >I/O Syscall Count</text> -<text id="details" x="10.00" y="245" > </text> -<text id="unzoom" x="10.00" y="24" class="hide">Reset Zoom</text> -<text id="search" x="1090.00" y="24" >Search</text> -<text id="ignorecase" x="1174.00" y="24" >ic</text> -<text id="matched" x="1090.00" y="245" > </text> -<g id="frames"> -<g > -<title>syscall`enter_newfstatat (219 samples, 0.02%)</title><rect x="385.2" y="101" width="0.2" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="388.23" y="111.5" ></text> -</g> -<g > -<title>/24.08 (116 samples, 0.01%)</title><rect x="1165.6" y="101" width="0.1" height="15.0" fill="rgb(233,130,30)" rx="2" ry="2" /> -<text x="1168.56" y="111.5" ></text> -</g> -<g > -<title>syscall`enter_read (136 samples, 0.01%)</title><rect x="853.2" y="85" width="0.2" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="856.23" y="95.5" ></text> -</g> -<g > -<title>/fd (148 samples, 0.01%)</title><rect x="509.8" y="165" width="0.1" height="15.0" fill="rgb(244,145,42)" rx="2" ry="2" /> -<text x="512.78" y="175.5" ></text> -</g> -<g > -<title>syscall`enter_fcntl (1,106 samples, 0.09%)</title><rect x="874.4" y="133" width="1.1" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="877.41" y="143.5" ></text> -</g> -<g > -<title>syscall`enter_close (245 samples, 0.02%)</title><rect x="722.9" y="165" width="0.2" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="725.91" y="175.5" ></text> -</g> -<g > -<title>syscall`enter_fcntl (356 samples, 0.03%)</title><rect x="849.7" y="101" width="0.3" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="852.67" y="111.5" ></text> -</g> -<g > -<title>/a1 (287 samples, 0.02%)</title><rect x="373.7" y="117" width="0.3" height="15.0" fill="rgb(221,125,18)" rx="2" ry="2" /> -<text x="376.71" y="127.5" ></text> -</g> -<g > -<title>/9b (240 samples, 0.02%)</title><rect x="372.4" y="117" width="0.2" height="15.0" fill="rgb(231,157,28)" rx="2" ry="2" /> -<text x="375.37" y="127.5" ></text> -</g> -<g > -<title>syscall`enter_write (435 samples, 0.04%)</title><rect x="449.5" y="181" width="0.4" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="452.48" y="191.5" ></text> -</g> -<g > -<title>syscall`enter_newfstatat (250 samples, 0.02%)</title><rect x="374.9" y="101" width="0.2" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="377.88" y="111.5" ></text> -</g> -<g > -<title>/telemetry (1,842 samples, 0.15%)</title><rect x="405.5" y="133" width="1.8" height="15.0" fill="rgb(243,127,42)" rx="2" ry="2" /> -<text x="408.47" y="143.5" ></text> -</g> -<g > -<title>/fd (2,472 samples, 0.20%)</title><rect x="633.2" y="165" width="2.4" height="15.0" fill="rgb(244,145,42)" rx="2" ry="2" /> -<text x="636.22" y="175.5" ></text> -</g> -<g > -<title>syscall`enter_openat (282 samples, 0.02%)</title><rect x="830.9" y="85" width="0.3" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="833.93" y="95.5" ></text> -</g> -<g > -<title>syscall`enter_newfstatat (263 samples, 0.02%)</title><rect x="368.1" y="101" width="0.3" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="371.14" y="111.5" ></text> -</g> -<g > -<title>/05 (283 samples, 0.02%)</title><rect x="337.2" y="117" width="0.2" height="15.0" fill="rgb(237,137,35)" rx="2" ry="2" /> -<text x="340.17" y="127.5" ></text> -</g> -<g > -<title>syscall`enter_newfstatat (236 samples, 0.02%)</title><rect x="359.0" y="101" width="0.2" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="362.01" y="111.5" ></text> -</g> -<g > -<title>syscall`enter_statx (411 samples, 0.03%)</title><rect x="972.2" y="149" width="0.4" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="975.21" y="159.5" ></text> -</g> -<g > -<title>syscall`enter_read (108 samples, 0.01%)</title><rect x="270.7" y="149" width="0.1" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="273.71" y="159.5" ></text> -</g> -<g > -<title>/fd (264 samples, 0.02%)</title><rect x="506.2" y="165" width="0.2" height="15.0" fill="rgb(244,145,42)" rx="2" ry="2" /> -<text x="509.19" y="175.5" ></text> -</g> -<g > -<title>syscall`enter_newfstatat (2,411 samples, 0.20%)</title><rect x="835.8" y="165" width="2.3" height="15.0" fill="rgb(230,196,28)" rx="2" ry="2" /> -<text x="838.78" y="175.5" ></text> -</g> -<g > -<title>syscall`enter_newfstatat (139 samples, 0.01%)</tit |
