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

Compute rect position and width from samples #210

Merged
merged 5 commits into from
May 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
77 changes: 35 additions & 42 deletions src/flamegraph/flamegraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ function init(evt) {
matchedtxt = document.getElementById("matched");
svg = document.getElementsByTagName("svg")[0];
frames = document.getElementById("frames");
total_samples = parseInt(frames.attributes.total_samples.value);
searching = 0;

// 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 + '"]')));
zoom(find_group(document.querySelector('[*|x="' + params.x + '"][y="' + params.y + '"]')));
if (params.s)
search(params.s);
};
Expand Down Expand Up @@ -71,9 +72,9 @@ window.addEventListener("click", function(e) {

// set parameters for zoom state
var el = target.querySelector("rect");
if (el && el.attributes && el.attributes.y && el.attributes._orig_x) {
if (el && el.attributes && el.attributes.y && el.attributes["fg:x"]) {
var params = get_params()
params.x = el.attributes._orig_x.value;
params.x = el.attributes["fg:x"].value;
jonhoo marked this conversation as resolved.
Show resolved Hide resolved
params.y = el.attributes.y.value;
history.replaceState(null, null, parse_params(params));
}
Expand Down Expand Up @@ -141,15 +142,15 @@ function find_group(node) {
return find_group(parent);
}
function orig_save(e, attr, val) {
if (e.attributes["_orig_" + attr] != undefined) return;
if (e.attributes["fg:orig_" + attr] != undefined) return;
if (e.attributes[attr] == undefined) return;
if (val == undefined) val = e.attributes[attr].value;
e.setAttribute("_orig_" + attr, val);
e.setAttribute("fg: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);
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;
Expand Down Expand Up @@ -197,42 +198,34 @@ function update_text(e) {
}
// zoom
function zoom_reset(e) {
if (e.attributes != undefined) {
orig_load(e, "x");
orig_load(e, "width");
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, ratio) {
if (e.attributes != undefined) {
if (e.attributes.x != undefined) {
orig_save(e, "x");
e.attributes.x.value = format_percent((parseFloat(e.attributes.x.value) - x) * ratio);
if (e.tagName == "text") {
e.attributes.x.value = format_percent(parseFloat(find_child(e.parentNode, "rect[x]").attributes.x.value) + (100 * 3 / frames.attributes.width.value));
}
}
if (e.attributes.width != undefined) {
orig_save(e, "width");
e.attributes.width.value = format_percent(parseFloat(e.attributes.width.value) * ratio);
}
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, ratio);
zoom_child(c[i], x, zoomed_width_samples);
}
}
function zoom_parent(e) {
if (e.attributes) {
if (e.attributes.x != undefined) {
orig_save(e, "x");
e.attributes.x.value = "0.0%";
}
if (e.attributes.width != undefined) {
orig_save(e, "width");
e.attributes.width.value = "100.0%";
}
}
Expand All @@ -243,20 +236,17 @@ function zoom_parent(e) {
}
function zoom(node) {
var attr = find_child(node, "rect").attributes;
var width = parseFloat(attr.width.value);
var xmin = parseFloat(attr.x.value);
var width = parseInt(attr["fg:w"].value);
var xmin = parseInt(attr["fg:x"].value);
var xmax = xmin + width;
var ymin = parseFloat(attr.y.value);
var ratio = 100 / width;
// XXX: Workaround for JavaScript float issues (fix me)
var fudge = 0.001;
unzoombtn.classList.remove("hide");
var el = 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 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;
Expand All @@ -265,7 +255,7 @@ function zoom(node) {
}
if (upstack) {
// Direct ancestor
if (ex <= xmin && (ex+ew+fudge) >= xmax) {
if (ex <= xmin && (ex+ew) >= xmax) {
e.classList.add("parent");
zoom_parent(e);
update_text(e);
Expand All @@ -277,11 +267,11 @@ function zoom(node) {
// Children maybe
else {
// no common path
if (ex < xmin || ex + fudge >= xmax) {
if (ex < xmin || ex >= xmax) {
e.classList.add("hide");
}
else {
zoom_child(e, xmin, ratio);
zoom_child(e, xmin, width);
update_text(e);
}
}
Expand Down Expand Up @@ -330,17 +320,21 @@ function search(term) {
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 = parseFloat(rect.attributes.width.value);
var w = parseInt(rect.attributes["fg:w"].value);
if (w > maxwidth)
maxwidth = w;
if (func.match(re)) {
// highlight
var x = parseFloat(rect.attributes.x.value);
var x = parseInt(rect.attributes["fg:x"].value);
orig_save(rect, "fill");
rect.attributes.fill.value = searchcolor;
// remember matches
Expand Down Expand Up @@ -380,11 +374,10 @@ function search(term) {
// 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 x = parseInt(keys[k]);
var w = matches[keys[k]];
if (x >= lastx + lastw - fudge) {
if (x >= lastx + lastw) {
count += w;
lastx = x;
lastw = w;
Expand Down
11 changes: 10 additions & 1 deletion src/flamegraph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,10 @@ impl Default for TextTruncateDirection {
}

struct Rectangle {
x1_samples: usize,
x1_pct: f64,
y1: usize,
x2_samples: usize,
x2_pct: f64,
y2: usize,
}
Expand Down Expand Up @@ -522,6 +524,7 @@ where
("id", "frames"),
("x", &container_x),
("width", &container_width),
("total_samples", &format!("{}", timemax)),
]),
))?;

Expand All @@ -547,8 +550,10 @@ where

let rect = Rectangle {
x1_pct,
x1_samples: frame.start_time,
y1,
x2_pct,
x2_samples: frame.end_time,
y2,
};

Expand Down Expand Up @@ -857,6 +862,8 @@ fn filled_rectangle<W: Write>(
let width = write!(buffer, "{:.4}%", rect.width_pct());
let height = write_usize(buffer, rect.height());
let color = write!(buffer, "rgb({},{},{})", color.r, color.g, color.b);
let x_samples = write_usize(buffer, rect.x1_samples);
let width_samples = write_usize(buffer, rect.x2_samples - rect.x1_samples);

if let Event::Empty(bytes_start) = cache_rect {
// clear the state
Expand All @@ -866,7 +873,9 @@ fn filled_rectangle<W: Write>(
"y" => &buffer[y],
"width" => &buffer[width],
"height" => &buffer[height],
"fill" => &buffer[color]
"fill" => &buffer[color],
"fg:x" => &buffer[x_samples],
"fg:w" => &buffer[width_samples]
));
} else {
unreachable!("cache wrapper was of wrong type: {:?}", cache_rect);
Expand Down
1 change: 1 addition & 0 deletions src/flamegraph/svg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ where
("viewBox", &*format!("0 0 {} {}", imagewidth, imageheight)),
("xmlns", "http://www.w3.org/2000/svg"),
("xmlns:xlink", "http://www.w3.org/1999/xlink"),
("xmlns:fg", "http://github.com/jonhoo/inferno"),
]),
))?;
svg.write_event(Event::Comment(BytesText::from_plain_str(
Expand Down