summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--simpleperf/command.cpp9
-rw-r--r--simpleperf/demo/README.md4
-rw-r--r--simpleperf/doc/README.md2
-rw-r--r--simpleperf/dso.cpp20
-rw-r--r--simpleperf/dso.h2
-rw-r--r--simpleperf/scripts/inferno/data_types.py19
-rw-r--r--simpleperf/scripts/inferno/inferno.py36
-rw-r--r--simpleperf/scripts/inferno/script.js232
-rw-r--r--simpleperf/scripts/inferno/svg_renderer.py96
9 files changed, 230 insertions, 190 deletions
diff --git a/simpleperf/command.cpp b/simpleperf/command.cpp
index 034163f9..0d071ab4 100644
--- a/simpleperf/command.cpp
+++ b/simpleperf/command.cpp
@@ -98,8 +98,15 @@ class CommandRegister {
CommandRegister command_register;
+static void StderrLogger(android::base::LogId, android::base::LogSeverity severity,
+ const char*, const char* file, unsigned int line, const char* message) {
+ static const char log_characters[] = "VDIWEFF";
+ char severity_char = log_characters[severity];
+ fprintf(stderr, "simpleperf %c %s:%u] %s\n", severity_char, file, line, message);
+}
+
bool RunSimpleperfCmd(int argc, char** argv) {
- android::base::InitLogging(argv, android::base::StderrLogger);
+ android::base::InitLogging(argv, StderrLogger);
std::vector<std::string> args;
android::base::LogSeverity log_severity = android::base::INFO;
diff --git a/simpleperf/demo/README.md b/simpleperf/demo/README.md
index 2bba0434..2c5f2b3f 100644
--- a/simpleperf/demo/README.md
+++ b/simpleperf/demo/README.md
@@ -10,8 +10,8 @@
## Introduction
Simpleperf is a native profiler used on Android platform. It can be used to profile Android
-applications. It's document is at [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/README.md).
-Instructions of preparing your Android application for profiling are [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/README.md#Android-application-profiling).
+applications. It's document is at [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/README.md).
+Instructions of preparing your Android application for profiling are [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/README.md#Android-application-profiling).
This directory is to show examples of using simpleperf to profile Android applications. The
meaning of each directory is as below:
diff --git a/simpleperf/doc/README.md b/simpleperf/doc/README.md
index 078d56b3..0b8d92a4 100644
--- a/simpleperf/doc/README.md
+++ b/simpleperf/doc/README.md
@@ -6,7 +6,7 @@ profile both Java and C++ code on Android. It can be used on Android L
and above.
Simpleperf is part of the Android Open Source Project. The source code is [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/).
-The latest document is [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/README.md).
+The latest document is [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/README.md).
Bugs and feature requests can be submitted at http://github.com/android-ndk/ndk/issues.
diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp
index 4f202bcd..90806406 100644
--- a/simpleperf/dso.cpp
+++ b/simpleperf/dso.cpp
@@ -153,7 +153,8 @@ Dso::Dso(DsoType type, const std::string& path, bool force_64bit)
min_vaddr_(std::numeric_limits<uint64_t>::max()),
is_loaded_(false),
dump_id_(UINT_MAX),
- symbol_dump_id_(0) {
+ symbol_dump_id_(0),
+ symbol_warning_loglevel_(android::base::WARNING) {
if (type_ == DSO_KERNEL) {
min_vaddr_ = 0;
}
@@ -287,6 +288,8 @@ void Dso::Load() {
// dumped_symbols, so later we can merge them with symbols read from file system.
dumped_symbols = std::move(symbols_);
symbols_.clear();
+ // Don't warn missing symbol table if we have dumped symbols in perf.data.
+ symbol_warning_loglevel_ = android::base::DEBUG;
}
bool result = false;
switch (type_) {
@@ -354,11 +357,10 @@ bool Dso::CheckReadSymbolResult(ElfStatus result, const std::string& filename) {
return true;
}
// Lacking symbol table isn't considered as an error but worth reporting.
- LOG(WARNING) << filename << " doesn't contain symbol table";
+ LOG(symbol_warning_loglevel_) << filename << " doesn't contain symbol table";
return true;
} else {
- LOG(WARNING) << "failed to read symbols from " << filename
- << ": " << result;
+ LOG(symbol_warning_loglevel_) << "failed to read symbols from " << filename << ": " << result;
return false;
}
}
@@ -380,7 +382,7 @@ bool Dso::LoadKernel() {
}
}
if (all_zero) {
- LOG(WARNING)
+ LOG(symbol_warning_loglevel_)
<< "Symbol addresses in /proc/kallsyms on device are all zero. "
"`echo 0 >/proc/sys/kernel/kptr_restrict` if possible.";
symbols_.clear();
@@ -396,8 +398,8 @@ bool Dso::LoadKernel() {
}
bool match = (build_id == real_build_id);
if (!match) {
- LOG(WARNING) << "failed to read symbols from /proc/kallsyms: Build id "
- << "mismatch";
+ LOG(symbol_warning_loglevel_) << "failed to read symbols from /proc/kallsyms: Build id "
+ << "mismatch";
return false;
}
}
@@ -417,8 +419,8 @@ bool Dso::LoadKernel() {
}
}
if (all_zero) {
- LOG(WARNING) << "Symbol addresses in /proc/kallsyms are all zero. "
- "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible.";
+ LOG(symbol_warning_loglevel_) << "Symbol addresses in /proc/kallsyms are all zero. "
+ "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible.";
symbols_.clear();
return false;
}
diff --git a/simpleperf/dso.h b/simpleperf/dso.h
index f41b1404..cb0e51d7 100644
--- a/simpleperf/dso.h
+++ b/simpleperf/dso.h
@@ -22,6 +22,7 @@
#include <unordered_map>
#include <vector>
+#include <android-base/logging.h>
#include <android-base/test_utils.h>
#include "build_id.h"
@@ -182,6 +183,7 @@ class Dso {
uint32_t dump_id_;
// Used to assign dump_id for symbols in current dso.
uint32_t symbol_dump_id_;
+ android::base::LogSeverity symbol_warning_loglevel_;
};
const char* DsoTypeToString(DsoType dso_type);
diff --git a/simpleperf/scripts/inferno/data_types.py b/simpleperf/scripts/inferno/data_types.py
index 5341d239..80e633f4 100644
--- a/simpleperf/scripts/inferno/data_types.py
+++ b/simpleperf/scripts/inferno/data_types.py
@@ -14,22 +14,23 @@
# limitations under the License.
#
+
class CallSite:
+
def __init__(self, ip, method, dso):
self.ip = ip
self.method = method
self.dso = dso
-
class Thread:
+
def __init__(self, tid):
self.tid = tid
self.samples = []
self.flamegraph = {}
self.num_samples = 0
-
def add_callchain(self, callchain, symbol, sample):
chain = []
self.num_samples += 1
@@ -42,15 +43,14 @@ class Thread:
chain.append(CallSite(sample.ip, symbol.symbol_name, symbol.dso_name))
self.samples.append(chain)
-
def collapse_flamegraph(self):
flamegraph = FlameGraphCallSite("root", "")
- flamegraph.id = 0 # This is used for wasd navigation, 0 = not a valid target.
+ flamegraph.id = 0 # This is used for wasd navigation, 0 = not a valid target.
self.flamegraph = flamegraph
for sample in self.samples:
flamegraph = self.flamegraph
for callsite in sample:
- flamegraph = flamegraph.get_callsite(callsite.method, callsite.dso)
+ flamegraph = flamegraph.get_callsite(callsite.method, callsite.dso)
# Populate root note.
for node in self.flamegraph.callsites:
@@ -58,6 +58,7 @@ class Thread:
class Process:
+
def __init__(self, name, pid):
self.name = name
self.pid = pid
@@ -73,6 +74,8 @@ class Process:
return self.threads[tid]
CALLSITE_COUNTER = 0
+
+
def get_callsite_id():
global CALLSITE_COUNTER
CALLSITE_COUNTER += 1
@@ -87,10 +90,9 @@ class FlameGraphCallSite:
self.method = method
self.dso = dso
self.num_samples = 0
- self.offset = 0 # Offset allows position nodes in different branches.
+ self.offset = 0 # Offset allows position nodes in different branches.
self.id = get_callsite_id()
-
def get_callsite(self, name, dso):
for c in self.callsites:
if c.equivalent(name, dso):
@@ -104,11 +106,10 @@ class FlameGraphCallSite:
def equivalent(self, method, dso):
return self.method == method and self.dso == dso
-
def get_max_depth(self):
max = 0
for c in self.callsites:
depth = c.get_max_depth()
if depth > max:
max = depth
- return max +1 \ No newline at end of file
+ return max + 1
diff --git a/simpleperf/scripts/inferno/inferno.py b/simpleperf/scripts/inferno/inferno.py
index 7510cb25..0b592965 100644
--- a/simpleperf/scripts/inferno/inferno.py
+++ b/simpleperf/scripts/inferno/inferno.py
@@ -46,6 +46,7 @@ except:
from data_types import *
from svg_renderer import *
+
def collect_data(args, process):
app_profiler_args = [sys.executable, "app_profiler.py", "-nb"]
if args.app:
@@ -158,20 +159,23 @@ def output_report(process):
Threads : %d <br/>
Samples : %d</br>
Duration: %s seconds<br/>""" % (
- process.name,process.pid,
- datetime.datetime.now().strftime("%Y-%m-%d (%A) %H:%M:%S"),
- len(process.threads),
- process.num_samples,
- process.args.capture_duration))
+ process.name, process.pid,
+ datetime.datetime.now().strftime("%Y-%m-%d (%A) %H:%M:%S"),
+ len(process.threads),
+ process.num_samples,
+ process.args.capture_duration))
if 'ro.product.model' in process.props:
- f.write("Machine : %s (%s) by %s<br/>" % (process.props["ro.product.model"],
- process.props["ro.product.name"], process.props["ro.product.manufacturer"]))
+ f.write(
+ "Machine : %s (%s) by %s<br/>" %
+ (process.props["ro.product.model"],
+ process.props["ro.product.name"],
+ process.props["ro.product.manufacturer"]))
if process.cmd:
f.write("Capture : %s<br/><br/>" % process.cmd)
f.write("</div>")
f.write("""<br/><br/>
<div>Navigate with WASD, zoom in with SPACE, zoom out with BACKSPACE.</div>""")
- f.write(get_local_asset_content("script.js"))
+ f.write("<script>%s</script>" % get_local_asset_content("script.js"))
# Output tid == pid Thread first.
main_thread = [x for _, x in process.threads.items() if x.tid == process.pid]
@@ -191,6 +195,7 @@ def output_report(process):
f.close()
return "file://" + filepath
+
def generate_flamegraph_offsets(flamegraph):
rover = flamegraph.offset
for callsite in flamegraph.callsites:
@@ -217,12 +222,11 @@ def open_report_in_browser(report_path):
browser_key = ""
for key, value in webbrowser._browsers.items():
if key.find("chrome") != -1:
- browser_key = key
+ browser_key = key
browser = webbrowser.get(browser_key)
browser.open(report_path, new=0, autoraise=True)
-
def main():
parser = argparse.ArgumentParser(description='Report samples in perf.data.')
@@ -240,7 +244,7 @@ def main():
parser.add_argument('-c', '--color', default='hot', choices=['hot', 'dso', 'legacy'],
help="""Color theme: hot=percentage of samples, dso=callsite DSO name,
legacy=brendan style""")
- parser.add_argument('-sc','--skip_collection', default=False, help='Skip data collection',
+ parser.add_argument('-sc', '--skip_collection', default=False, help='Skip data collection',
action="store_true")
parser.add_argument('-nc', '--skip_recompile', action='store_true', help="""When profiling
an Android app, by default we recompile java bytecode to native
@@ -248,8 +252,12 @@ def main():
if the code has been compiled or you don't need to profile java code.""")
parser.add_argument('-f', '--sample_frequency', type=int, default=6000, help='Sample frequency')
parser.add_argument('-w', '--svg_width', type=int, default=1124)
- parser.add_argument('-du', '--dwarf_unwinding', help='Perform unwinding using dwarf instead of fp.',
- default=False, action='store_true')
+ parser.add_argument(
+ '-du',
+ '--dwarf_unwinding',
+ help='Perform unwinding using dwarf instead of fp.',
+ default=False,
+ action='store_true')
parser.add_argument('-e', '--events', help="""Sample based on event occurences instead of
frequency. Format expected is "event_counts event_name".
e.g: "10000 cpu-cyles". A few examples of event_name: cpu-cycles,
@@ -283,4 +291,4 @@ def main():
log_info("Report generated at '%s'." % report_path)
if __name__ == "__main__":
- main() \ No newline at end of file
+ main()
diff --git a/simpleperf/scripts/inferno/script.js b/simpleperf/scripts/inferno/script.js
index 6a81d731..3288d8be 100644
--- a/simpleperf/scripts/inferno/script.js
+++ b/simpleperf/scripts/inferno/script.js
@@ -1,146 +1,141 @@
-<script type="text/ecmascript">
+'use strict';
+
function init() {
- var x = document.getElementsByTagName("svg")
- for (i = 0; i < x.length; i=i+1) {
+ let x = document.getElementsByTagName('svg');
+ for (let i = 0; i < x.length; i++) {
createZoomHistoryStack(x[i]);
}
}
// Create a stack add the root svg element in it.
function createZoomHistoryStack(svgElement) {
- stack = [];
- svgElement.zoomStack = stack;
- stack.push(svgElement.getElementById(svgElement.attributes["rootid"].value))
+ svgElement.zoomStack = [svgElement.getElementById(svgElement.attributes['rootid'].value)];
}
function dumpStack(svgElement) {
// Disable (enable for debugging)
- return
+ return;
stack = svgElement.zoomStack;
- for (i=0 ; i < stack.length; i++) {
- title = stack[i].getElementsByTagName("title")[0];
- console.log("[" +i+ "]-" + title.textContent)
+ for (i=0; i < stack.length; i++) {
+ let title = stack[i].getElementsByTagName('title')[0];
+ console.log('[' +i+ ']-' + title.textContent);
}
}
function adjust_node_text_size(x) {
- title = x.getElementsByTagName("title")[0];
- text = x.getElementsByTagName("text")[0];
- rect = x.getElementsByTagName("rect")[0];
+ let title = x.getElementsByTagName('title')[0];
+ let text = x.getElementsByTagName('text')[0];
+ let rect = x.getElementsByTagName('rect')[0];
- width = parseFloat(rect.attributes["width"].value);
+ let width = parseFloat(rect.attributes['width'].value);
// Don't even bother trying to find a best fit. The area is too small.
if (width < 25) {
- text.textContent = "";
+ text.textContent = '';
return;
}
// Remove dso and #samples which are here only for mouseover purposes.
- methodName = title.textContent.substring(0, title.textContent.indexOf("|"));
+ let methodName = title.textContent.split(' | ')[0];
- var numCharacters;
- for (numCharacters=methodName.length; numCharacters>4; numCharacters--) {
+ let numCharacters;
+ for (numCharacters = methodName.length; numCharacters > 4; numCharacters--) {
// Avoid reflow by using hard-coded estimate instead of text.getSubStringLength(0, numCharacters)
// if (text.getSubStringLength(0, numCharacters) <= width) {
if (numCharacters * 7.5 <= width) {
- break ;
+ break;
}
}
if (numCharacters == methodName.length) {
text.textContent = methodName;
- return
+ return;
}
- text.textContent = methodName.substring(0, numCharacters-2) + "..";
+ text.textContent = methodName.substring(0, numCharacters-2) + '..';
}
function adjust_text_size(svgElement) {
- var x = svgElement.getElementsByTagName("g");
- var i;
- for (i=0 ; i < x.length ; i=i+1) {
- adjust_node_text_size(x[i])
+ let x = svgElement.getElementsByTagName('g');
+ for (let i = 0; i < x.length; i++) {
+ adjust_node_text_size(x[i]);
}
}
function zoom(e) {
- svgElement = e.ownerSVGElement
- zoomStack = svgElement.zoomStack;
+ let svgElement = e.ownerSVGElement;
+ let zoomStack = svgElement.zoomStack;
zoomStack.push(e);
- displayFromElement(e)
+ displayFromElement(e);
select(e);
- dumpStack(e.ownerSVGElement);
+ dumpStack(svgElement);
// Show zoom out button.
- svgElement.getElementById("zoom_rect").style.display = "block";
- svgElement.getElementById("zoom_text").style.display = "block";
+ svgElement.getElementById('zoom_rect').style.display = 'block';
+ svgElement.getElementById('zoom_text').style.display = 'block';
}
function displayFromElement(e) {
- var clicked_rect = e.getElementsByTagName("rect")[0];
- var clicked_origin_x = clicked_rect.attributes["ox"].value;
- var clicked_origin_y = clicked_rect.attributes["oy"].value;
- var clicked_origin_width = clicked_rect.attributes["owidth"].value;
+ let clicked_rect = e.getElementsByTagName('rect')[0];
+ let clicked_origin_x = clicked_rect.attributes['ox'].value;
+ let clicked_origin_y = clicked_rect.attributes['oy'].value;
+ let clicked_origin_width = clicked_rect.attributes['owidth'].value;
- var svgBox = e.ownerSVGElement.getBoundingClientRect();
- var svgBoxHeight = svgBox.height
- var svgBoxWidth = svgBox.width
- var scaleFactor = svgBoxWidth/clicked_origin_width;
+ let svgBox = e.ownerSVGElement.getBoundingClientRect();
+ let svgBoxHeight = svgBox.height;
+ let svgBoxWidth = svgBox.width;
+ let scaleFactor = svgBoxWidth / clicked_origin_width;
- var callsites = e.ownerSVGElement.getElementsByTagName("g");
- var i;
- for (i = 0; i < callsites.length; i=i+1) {
- text = callsites[i].getElementsByTagName("text")[0];
- rect = callsites[i].getElementsByTagName("rect")[0];
+ let callsites = e.ownerSVGElement.getElementsByTagName('g');
+ for (let i = 0; i < callsites.length; i++) {
+ let text = callsites[i].getElementsByTagName('text')[0];
+ let rect = callsites[i].getElementsByTagName('rect')[0];
- rect_o_x = rect.attributes["ox"].value
- rect_o_y = parseFloat(rect.attributes["oy"].value)
+ let rect_o_x = parseFloat(rect.attributes['ox'].value);
+ let rect_o_y = parseFloat(rect.attributes['oy'].value);
// Avoid multiple forced reflow by hiding nodes.
if (rect_o_y > clicked_origin_y) {
- rect.style.display = "none"
- text.style.display = "none"
- continue;
- } else {
- rect.style.display = "block"
- text.style.display = "block"
+ rect.style.display = 'none';
+ text.style.display = 'none';
+ continue;
}
+ rect.style.display = 'block';
+ text.style.display = 'block';
- rect.attributes["x"].value = newrec_x = (rect_o_x - clicked_origin_x) * scaleFactor ;
- rect.attributes["y"].value = newrec_y = rect_o_y + (svgBoxHeight - clicked_origin_y - 17 -2);
+ let newrec_x = rect.attributes['x'].value = (rect_o_x - clicked_origin_x) * scaleFactor;
+ let newrec_y = rect.attributes['y'].value = rect_o_y + (svgBoxHeight - clicked_origin_y
+ - 17 - 2);
- text.attributes["y"].value = newrec_y + 12;
- text.attributes["x"].value = newrec_x + 4;
+ text.attributes['y'].value = newrec_y + 12;
+ text.attributes['x'].value = newrec_x + 4;
- rect.attributes["width"].value = rect.attributes["owidth"].value * scaleFactor;
+ rect.attributes['width'].value = rect.attributes['owidth'].value * scaleFactor;
}
adjust_text_size(e.ownerSVGElement);
-
}
function unzoom(e) {
-
- var svgOwner = e.ownerSVGElement;
- stack = svgOwner.zoomStack;
+ let svgOwner = e.ownerSVGElement;
+ let stack = svgOwner.zoomStack;
// Unhighlight whatever was selected.
- if (selected != null)
- selected.classList.remove("s")
-
+ if (selected) {
+ selected.classList.remove('s');
+ }
// Stack management: Never remove the last element which is the flamegraph root.
if (stack.length > 1) {
- previouslySelected = stack.pop();
+ let previouslySelected = stack.pop();
select(previouslySelected);
}
- nextElement = stack[stack.length-1] // stack.peek()
+ let nextElement = stack[stack.length-1];
// Hide zoom out button.
- if (stack.length==1) {
- svgOwner.getElementById("zoom_rect").style.display = "none";
- svgOwner.getElementById("zoom_text").style.display = "none";
+ if (stack.length == 1) {
+ svgOwner.getElementById('zoom_rect').style.display = 'none';
+ svgOwner.getElementById('zoom_text').style.display = 'none';
}
displayFromElement(nextElement);
@@ -148,97 +143,96 @@ function unzoom(e) {
}
function search(e) {
- var term = prompt("Search for:", "");
-
- var svgOwner = e.ownerSVGElement
- var callsites = e.ownerSVGElement.getElementsByTagName("g");
-
- if (term == null || term == "") {
- for (i = 0; i < callsites.length; i=i+1) {
- rect = callsites[i].getElementsByTagName("rect")[0];
- rect.attributes["fill"].value = rect.attributes["ofill"].value;
+ let term = prompt('Search for:', '');
+ let svgOwner = e.ownerSVGElement;
+ let callsites = e.ownerSVGElement.getElementsByTagName('g');
+
+ if (!term) {
+ for (let i = 0; i < callsites.length; i++) {
+ let rect = callsites[i].getElementsByTagName('rect')[0];
+ rect.attributes['fill'].value = rect.attributes['ofill'].value;
}
return;
}
- for (i = 0; i < callsites.length; i=i+1) {
- title = callsites[i].getElementsByTagName("title")[0];
- rect = callsites[i].getElementsByTagName("rect")[0];
+ for (let i = 0; i < callsites.length; i++) {
+ let title = callsites[i].getElementsByTagName('title')[0];
+ let rect = callsites[i].getElementsByTagName('rect')[0];
if (title.textContent.indexOf(term) != -1) {
- rect.attributes["fill"].value = "rgb(230,100,230)";
+ rect.attributes['fill'].value = 'rgb(230,100,230)';
} else {
- rect.attributes["fill"].value = rect.attributes["ofill"].value;
+ rect.attributes['fill'].value = rect.attributes['ofill'].value;
}
}
}
-var selected;
+let selected;
document.onkeydown = function handle_keyboard_input(e) {
- if (selected == null)
+ if (!selected) {
return;
+ }
- title = selected.getElementsByTagName("title")[0];
- nav = selected.attributes["nav"].value.split(",")
- navigation_index = -1
+ let title = selected.getElementsByTagName('title')[0];
+ let nav = selected.attributes['nav'].value.split(',');
+ let navigation_index;
switch (e.keyCode) {
- //case 38: // ARROW UP
- case 87 : navigation_index = 0;break; //W
+ // case 38: // ARROW UP
+ case 87: navigation_index = 0; break; // W
- //case 32 : // ARROW LEFT
- case 65 : navigation_index = 1;break; //A
+ // case 32 : // ARROW LEFT
+ case 65: navigation_index = 1; break; // A
// case 43: // ARROW DOWN
- case 68 : navigation_index = 3;break; // S
+ case 68: navigation_index = 3; break; // S
// case 39: // ARROW RIGHT
- case 83 : navigation_index = 2;break; // D
+ case 83: navigation_index = 2; break; // D
- case 32 : zoom(selected); return false; break; // SPACE
+ case 32: zoom(selected); return false; // SPACE
case 8: // BACKSPACE
unzoom(selected); return false;
default: return true;
}
- if (nav[navigation_index] == "0")
+ if (nav[navigation_index] == '0') {
return false;
+ }
- target_element = selected.ownerSVGElement.getElementById(nav[navigation_index]);
- select(target_element)
+ let target_element = selected.ownerSVGElement.getElementById(nav[navigation_index]);
+ select(target_element);
return false;
-}
+};
function select(e) {
- if (selected != null)
- selected.classList.remove("s")
+ if (selected) {
+ selected.classList.remove('s');
+ }
selected = e;
- selected.classList.add("s")
+ selected.classList.add('s');
// Update info bar
- titleElement = selected.getElementsByTagName("title")[0];
- text = titleElement.textContent;
+ let titleElement = selected.getElementsByTagName('title')[0];
+ let text = titleElement.textContent;
// Parse title
- method_and_info = text.split(" | ");
- methodName = method_and_info[0];
- info = method_and_info[1]
+ let method_and_info = text.split(' | ');
+ let methodName = method_and_info[0];
+ let info = method_and_info[1];
// Parse info
// '/system/lib64/libhwbinder.so (4 samples: 0.28%)'
- var regexp = /(.*) \(.* ([0-9**\.[0-9]*%)\)/g;
- match = regexp.exec(info);
+ let regexp = /(.*) \(.* ([0-9**\.[0-9]*%)\)/g;
+ let match = regexp.exec(info);
if (match.length > 2) {
- percentage = match[2]
+ let percentage = match[2];
// Write percentage
- percentageTextElement = selected.ownerSVGElement.getElementById("percent_text")
- percentageTextElement.textContent = percentage
- //console.log("'" + percentage + "'")
+ let percentageTextElement = selected.ownerSVGElement.getElementById('percent_text');
+ percentageTextElement.textContent = percentage;
+ // console.log("'" + percentage + "'")
}
// Set fields
- barTextElement = selected.ownerSVGElement.getElementById("info_text")
- barTextElement.textContent = methodName
-}
-
-
-</script> \ No newline at end of file
+ let barTextElement = selected.ownerSVGElement.getElementById('info_text');
+ barTextElement.textContent = methodName;
+} \ No newline at end of file
diff --git a/simpleperf/scripts/inferno/svg_renderer.py b/simpleperf/scripts/inferno/svg_renderer.py
index 00f2d759..68559499 100644
--- a/simpleperf/scripts/inferno/svg_renderer.py
+++ b/simpleperf/scripts/inferno/svg_renderer.py
@@ -22,36 +22,37 @@ FONT_SIZE = 12
def hash_to_float(string):
- return hash(string) / float(sys.maxint)
+ return hash(string) / float(sys.maxsize)
-def getLegacyColor(method) :
+
+def getLegacyColor(method):
r = 175 + int(50 * hash_to_float(reversed(method)))
g = 60 + int(180 * hash_to_float(method))
- b = 60 +int(55 * hash_to_float(reversed(method)))
- return (r,g,b)
+ b = 60 + int(55 * hash_to_float(reversed(method)))
+ return (r, g, b)
-def getDSOColor(method) :
+def getDSOColor(method):
r = 170 + int(80 * hash_to_float(reversed(method)))
- g = 180 +int(70 * hash_to_float((method)))
+ g = 180 + int(70 * hash_to_float((method)))
b = 170 + int(80 * hash_to_float(reversed(method)))
- return (r,g,b)
+ return (r, g, b)
-def getHeatColor(callsite, num_samples) :
- r = 245 + 10* (1- float(callsite.num_samples)/ num_samples)
- g = 110 + 105* (1-float(callsite.num_samples)/ num_samples)
+def getHeatColor(callsite, num_samples):
+ r = 245 + 10 * (1 - float(callsite.num_samples) / num_samples)
+ g = 110 + 105 * (1 - float(callsite.num_samples) / num_samples)
b = 100
- return (r,g,b)
+ return (r, g, b)
def createSVGNode(callsite, depth, f, num_samples, height, color_scheme, nav):
- x = float(callsite.offset)/float(num_samples)*SVG_CANVAS_WIDTH
+ x = float(callsite.offset) / float(num_samples) * SVG_CANVAS_WIDTH
y = height - (depth * SVG_NODE_HEIGHT) - SVG_NODE_HEIGHT
- width = float(callsite.num_samples) /float(num_samples) * SVG_CANVAS_WIDTH
+ width = float(callsite.num_samples) / float(num_samples) * SVG_CANVAS_WIDTH
method = callsite.method.replace(">", "&gt;").replace("<", "&lt;")
- if (width <= 0) :
+ if (width <= 0):
return
if color_scheme == "dso":
@@ -61,8 +62,6 @@ def createSVGNode(callsite, depth, f, num_samples, height, color_scheme, nav):
else:
r, g, b = getHeatColor(callsite, num_samples)
-
-
r_border = (r - 50)
if r_border < 0:
r_border = 0
@@ -76,15 +75,41 @@ def createSVGNode(callsite, depth, f, num_samples, height, color_scheme, nav):
b_border = 0
f.write(
- '<g id=%d class="n" onclick="zoom(this);" onmouseenter="select(this);" nav="%s"> \n\
+ '<g id=%d class="n" onclick="zoom(this);" onmouseenter="select(this);" nav="%s"> \n\
<title>%s | %s (%d samples: %3.2f%%)</title>\n \
<rect x="%f" y="%f" ox="%f" oy="%f" width="%f" owidth="%f" height="15.0" ofill="rgb(%d,%d,%d)" \
fill="rgb(%d,%d,%d)" style="stroke:rgb(%d,%d,%d)"/>\n \
<text x="%f" y="%f" font-size="%d" font-family="Monospace"></text>\n \
- </g>\n' % (callsite.id, ','.join(str(x) for x in nav),
- method, callsite.dso, callsite.num_samples, callsite.num_samples/float(num_samples) * 100,
- x, y, x, y, width , width, r, g, b, r, g, b, r_border, g_border, b_border,
- x+2, y+12, FONT_SIZE))
+ </g>\n' %
+ (callsite.id,
+ ','.join(
+ str(x) for x in nav),
+ method,
+ callsite.dso,
+ callsite.num_samples,
+ callsite.num_samples /
+ float(num_samples) *
+ 100,
+ x,
+ y,
+ x,
+ y,
+ width,
+ width,
+ r,
+ g,
+ b,
+ r,
+ g,
+ b,
+ r_border,
+ g_border,
+ b_border,
+ x +
+ 2,
+ y +
+ 12,
+ FONT_SIZE))
def renderSVGNodes(flamegraph, depth, f, num_samples, height, color_scheme):
@@ -94,13 +119,12 @@ def renderSVGNodes(flamegraph, depth, f, num_samples, height, color_scheme):
if i == 0:
left_index = 0
else:
- left_index = flamegraph.callsites[i-1].id
+ left_index = flamegraph.callsites[i - 1].id
- if i == len(flamegraph.callsites)-1:
+ if i == len(flamegraph.callsites) - 1:
right_index = 0
else:
- right_index = flamegraph.callsites[i+1].id
-
+ right_index = flamegraph.callsites[i + 1].id
up_index = 0
max_up = 0
@@ -110,19 +134,19 @@ def renderSVGNodes(flamegraph, depth, f, num_samples, height, color_scheme):
up_index = upcallsite.id
# up, left, down, right
- nav = [up_index, left_index,flamegraph.id,right_index]
+ nav = [up_index, left_index, flamegraph.id, right_index]
createSVGNode(callsite, depth, f, num_samples, height, color_scheme, nav)
# Recurse down
- renderSVGNodes(callsite, depth+1, f, num_samples, height, color_scheme)
+ renderSVGNodes(callsite, depth + 1, f, num_samples, height, color_scheme)
+
def renderSearchNode(f):
f.write(
- '<rect id="search_rect" style="stroke:rgb(0,0,0);" onclick="search(this);" class="t" rx="10" ry="10" \
+ '<rect id="search_rect" style="stroke:rgb(0,0,0);" onclick="search(this);" class="t" rx="10" ry="10" \
x="%d" y="10" width="80" height="30" fill="rgb(255,255,255)""/> \
- <text id="search_text" class="t" x="%d" y="30" onclick="search(this);">Search</text>\n'
- % (SVG_CANVAS_WIDTH - 95, SVG_CANVAS_WIDTH - 80)
- )
+ <text id="search_text" class="t" x="%d" y="30" onclick="search(this);">Search</text>\n' %
+ (SVG_CANVAS_WIDTH - 95, SVG_CANVAS_WIDTH - 80))
def renderUnzoomNode(f):
@@ -133,6 +157,7 @@ def renderUnzoomNode(f):
onclick="unzoom(this);">Zoom out</text>\n'
)
+
def renderInfoNode(f):
f.write(
'<clipPath id="info_clip_path"> <rect id="info_rect" style="stroke:rgb(0,0,0);" \
@@ -142,28 +167,29 @@ def renderInfoNode(f):
<text clip-path="url(#info_clip_path)" id="info_text" x="128" y="30"></text>\n' % (SVG_CANVAS_WIDTH - 335, SVG_CANVAS_WIDTH - 325)
)
+
def renderPercentNode(f):
f.write(
'<rect id="percent_rect" style="stroke:rgb(0,0,0);" \
rx="10" ry="10" x="%d" y="10" width="82" height="30" fill="rgb(255,255,255)"/> \
- <text id="percent_text" text-anchor="end" x="%d" y="30">100.00%%</text>\n' % (SVG_CANVAS_WIDTH - (95 * 2),SVG_CANVAS_WIDTH - (125))
+ <text id="percent_text" text-anchor="end" x="%d" y="30">100.00%%</text>\n' % (SVG_CANVAS_WIDTH - (95 * 2), SVG_CANVAS_WIDTH - (125))
)
def renderSVG(flamegraph, f, color_scheme, width):
global SVG_CANVAS_WIDTH
SVG_CANVAS_WIDTH = width
- height = (flamegraph.get_max_depth() + 2 )* SVG_NODE_HEIGHT
+ height = (flamegraph.get_max_depth() + 2) * SVG_NODE_HEIGHT
f.write('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" \
width="%d" height="%d" style="border: 1px solid black;" \
onload="adjust_text_size(this);" rootid="%d">\n' % (SVG_CANVAS_WIDTH, height, flamegraph.callsites[0].id))
f.write('<defs > <linearGradient id="background_gradiant" y1="0" y2="1" x1="0" x2="0" > \
<stop stop-color="#eeeeee" offset="5%" /> <stop stop-color="#efefb1" offset="90%" /> </linearGradient> </defs>')
- f.write('<rect x="0.0" y="0" width="%d" height="%d" fill="url(#background_gradiant)" />' % \
+ f.write('<rect x="0.0" y="0" width="%d" height="%d" fill="url(#background_gradiant)" />' %
(SVG_CANVAS_WIDTH, height))
renderSVGNodes(flamegraph, 0, f, flamegraph.num_samples, height, color_scheme)
renderSearchNode(f)
renderUnzoomNode(f)
renderInfoNode(f)
renderPercentNode(f)
- f.write("</svg><br/>\n\n") \ No newline at end of file
+ f.write("</svg><br/>\n\n")