Skip to content

Commit

Permalink
#864: Reduce Flame Graph size
Browse files Browse the repository at this point in the history
  • Loading branch information
apangin committed Dec 16, 2023
1 parent f099abe commit 9e874cb
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 34 deletions.
51 changes: 42 additions & 9 deletions src/converter/FlameGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public class FlameGraph implements Comparator<FlameGraph.Frame> {
private final Frame root = new Frame(getFrameKey("", FRAME_NATIVE));
private int[] order;
private int depth;
private int lastLevel;
private long lastX;
private long lastTotal;
private long mintotal;

public FlameGraph(Arguments args) {
Expand Down Expand Up @@ -146,25 +149,45 @@ private void printCpool(PrintStream out) {
out.print("'all'");

order = new int[strings.length];
String s = "";
for (int i = 1; i < strings.length; i++) {
order[cpool.get(strings[i])] = i;
out.print(",\n'" + escape(strings[i]) + "'");
int prefixLen = Math.min(getCommonPrefix(s, s = strings[i]), 95);
out.print(",\n'" + escape((char) (prefixLen + ' ') + s.substring(prefixLen)) + "'");
order[cpool.get(s)] = i;
}

// cpool is not used beyond this point
cpool.clear();
}

private void printFrame(PrintStream out, Frame frame, int level, long x) {
int type = frame.getType();
int titleIndex = order[frame.getTitleIndex()];
int nameAndType = order[frame.getTitleIndex()] << 3 | frame.getType();
boolean hasExtraTypes = (frame.inlined | frame.c1 | frame.interpreted) != 0 &&
frame.inlined < frame.total && frame.interpreted < frame.total;

char func = 'f';
if (level == lastLevel + 1 && x == lastX) {
func = 'u';
} else if (level == lastLevel && x == lastX + lastTotal) {
func = 'n';
}

if ((frame.inlined | frame.c1 | frame.interpreted) != 0 && frame.inlined < frame.total && frame.interpreted < frame.total) {
out.println("f(" + level + "," + x + "," + frame.total + "," + type + "," + titleIndex + "," +
frame.inlined + "," + frame.c1 + "," + frame.interpreted + ")");
} else {
out.println("f(" + level + "," + x + "," + frame.total + "," + type + "," + titleIndex + ")");
StringBuilder sb = new StringBuilder(24).append(func).append('(').append(nameAndType);
if (func == 'f') {
sb.append(',').append(level).append(',').append(x - lastX);
}
if (frame.total != lastTotal || hasExtraTypes) {
sb.append(',').append(frame.total);
if (hasExtraTypes) {
sb.append(',').append(frame.inlined).append(',').append(frame.c1).append(',').append(frame.interpreted);
}
}
sb.append(')');
out.println(sb.toString());

lastLevel = level;
lastX = x;
lastTotal = frame.total;

Frame[] children = frame.values().toArray(Frame.EMPTY_ARRAY);
Arrays.sort(children, this);
Expand Down Expand Up @@ -243,6 +266,16 @@ private Frame addChild(Frame frame, String title, long ticks) {
return child;
}

static int getCommonPrefix(String a, String b) {
int length = Math.min(a.length(), b.length());
for (int i = 0; i < length; i++) {
if (a.charAt(i) != b.charAt(i) || a.charAt(i) > 127) {
return i;
}
}
return length;
}

static String stripSuffix(String title) {
return title.substring(0, title.length() - 4);
}
Expand Down
63 changes: 46 additions & 17 deletions src/flameGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ class StringUtils {
s.replace(i, 1, replacement, rlen);
}
}

static size_t getCommonPrefix(const std::string& a, const std::string& b) {
size_t length = a.size() < b.size() ? a.size() : b.size();
for (size_t i = 0; i < length; i++) {
if (a[i] != b[i] || a[i] > 127) {
return i;
}
}
return length;
}
};


Expand Down Expand Up @@ -169,18 +179,33 @@ void FlameGraph::dump(std::ostream& out, bool tree) {
}

void FlameGraph::printFrame(std::ostream& out, u32 key, const Trie& f, int level, u64 x) {
FrameTypeId type = f.type(key);
u32 name_index = _name_order[f.nameIndex(key)];

if (f._inlined | f._c1_compiled | f._interpreted) {
snprintf(_buf, sizeof(_buf) - 1, "f(%d,%llu,%llu,%u,%u,%llu,%llu,%llu)\n",
level, x, f._total, type, name_index, f._inlined, f._c1_compiled, f._interpreted);
u32 name_and_type = _name_order[f.nameIndex(key)] << 3 | f.type(key);
bool has_extra_types = (f._inlined | f._c1_compiled | f._interpreted) &&
f._inlined < f._total && f._interpreted < f._total;

char* p = _buf;
if (level == _last_level + 1 && x == _last_x) {
p += snprintf(p, 100, "u(%u", name_and_type);
} else if (level == _last_level && x == _last_x + _last_total) {
p += snprintf(p, 100, "n(%u", name_and_type);
} else {
snprintf(_buf, sizeof(_buf) - 1, "f(%d,%llu,%llu,%u,%u)\n",
level, x, f._total, type, name_index);
p += snprintf(p, 100, "f(%u,%d,%llu", name_and_type, level, x - _last_x);
}

if (f._total != _last_total || has_extra_types) {
p += snprintf(p, 100, ",%llu", f._total);
if (has_extra_types) {
p += snprintf(p, 100, ",%llu,%llu,%llu", f._inlined, f._c1_compiled, f._interpreted);
}
}

strcpy(p, ")\n");
out << _buf;

_last_level = level;
_last_x = x;
_last_total = f._total;

if (f._children.empty()) {
return;
}
Expand Down Expand Up @@ -252,19 +277,23 @@ void FlameGraph::printTreeFrame(std::ostream& out, const Trie& f, int level, con

void FlameGraph::printCpool(std::ostream& out) {
out << "'all'";

std::string prev;
u32 index = 0;
for (std::map<std::string, u32>::const_iterator it = _cpool.begin(); it != _cpool.end(); ++it) {
if (_name_order[it->second]) {
_name_order[it->second] = ++index;
if (it->first.find('\'') == std::string::npos && it->first.find('\\') == std::string::npos) {
// Common case: no replacement needed
out << ",\n'" << it->first << "'";
} else {
std::string s = it->first;
StringUtils::replace(s, '\'', "\\'", 2);
StringUtils::replace(s, '\\', "\\\\", 2);
out << ",\n'" << s << "'";
}

size_t prefix_len = StringUtils::getCommonPrefix(prev, it->first);
prev = it->first;

if (prefix_len > 95) prefix_len = 95;
std::string s(1, (char)(prefix_len + ' '));
s.append(it->first, prefix_len, std::string::npos);

StringUtils::replace(s, '\\', "\\\\", 2);
StringUtils::replace(s, '\'', "\\'", 2);
out << ",\n'" << s << "'";
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/flameGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ class FlameGraph {
double _minwidth;
bool _reverse;

int _last_level;
u64 _last_x;
u64 _last_total;

void printFrame(std::ostream& out, u32 key, const Trie& f, int level, u64 x);
void printTreeFrame(std::ostream& out, const Trie& f, int level, const char** names);
void printCpool(std::ostream& out);
Expand All @@ -94,7 +98,10 @@ class FlameGraph {
_title(title),
_counter(counter),
_minwidth(minwidth),
_reverse(reverse) {
_reverse(reverse),
_last_level(0),
_last_x(0),
_last_total(0) {
_buf[sizeof(_buf) - 1] = 0;
}

Expand Down
31 changes: 24 additions & 7 deletions src/res/flame.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ <h1>/*title:*/</h1>
// Copyright 2020 Andrei Pangin
// Licensed under the Apache License, Version 2.0.
'use strict';
var root, rootLevel, px, pattern;
var reverse = /*reverse:*/false;
let root, rootLevel, px, pattern;
let level0 = 0, left0 = 0, width0 = 0;
let reverse = /*reverse:*/false;
const levels = Array(/*depth:*/0);
for (let h = 0; h < levels.length; h++) {
levels[h] = [];
Expand Down Expand Up @@ -64,12 +65,21 @@ <h1>/*title:*/</h1>
return '#' + (p[0] + ((p[1] * v) << 16 | (p[2] * v) << 8 | (p[3] * v))).toString(16);
}

function f(level, left, width, type, title, inln, c1, int) {
levels[level].push({left: left, width: width, color: getColor(palette[type]), title: cpool[title],
function f(key, level, left, width, inln, c1, int) {
levels[level0 = level].push({left: left0 += left, width: width0 = width || width0,
color: getColor(palette[key & 7]), title: cpool[key >>> 3],
details: (int ? ', int=' + int : '') + (c1 ? ', c1=' + c1 : '') + (inln ? ', inln=' + inln : '')
});
}

function u(key, width, inln, c1, int) {
f(key, level0 + 1, 0, width, inln, c1, int)
}

function n(key, width, inln, c1, int) {
f(key, level0, width0, width, inln, c1, int)
}

function samples(n) {
return n === 1 ? '1 sample' : n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ' samples';
}
Expand Down Expand Up @@ -172,12 +182,18 @@ <h1>/*title:*/</h1>
return totalMarked();
}

function unpack(cpool) {
for (let i = 1; i < cpool.length; i++) {
cpool[i] = cpool[i - 1].substring(0, cpool[i].charCodeAt(0) - 32) + cpool[i].substring(1);
}
}

canvas.onmousemove = function() {
const h = Math.floor((reverse ? event.offsetY : (canvasHeight - event.offsetY)) / 16);
if (h >= 0 && h < levels.length) {
const f = findFrame(levels[h], event.offsetX / px + root.left);
if (f) {
if (f != root) getSelection().removeAllRanges();
if (f !== root) getSelection().removeAllRanges();
hl.style.left = (Math.max(f.left - root.left, 0) * px + canvas.offsetLeft) + 'px';
hl.style.width = (Math.min(f.width, root.width) * px) + 'px';
hl.style.top = ((reverse ? h * 16 : canvasHeight - (h + 1) * 16) + canvas.offsetTop) + 'px';
Expand All @@ -186,7 +202,7 @@ <h1>/*title:*/</h1>
canvas.title = f.title + '\n(' + samples(f.width) + f.details + ', ' + pct(f.width, levels[0][0].width) + '%)';
canvas.style.cursor = 'pointer';
canvas.onclick = function() {
if (f != root) {
if (f !== root) {
render(f, h);
canvas.onmousemove();
}
Expand All @@ -204,7 +220,7 @@ <h1>/*title:*/</h1>
status.style.display = 'none';
canvas.title = '';
canvas.style.cursor = '';
canvas.onclick = '';
canvas.onclick = null;
}

canvas.ondblclick = function() {
Expand Down Expand Up @@ -236,6 +252,7 @@ <h1>/*title:*/</h1>
const cpool = [
/*cpool:*/
];
unpack(cpool);

/*frames:*/
search(/*highlight:*/);
Expand Down

0 comments on commit 9e874cb

Please sign in to comment.