summaryrefslogtreecommitdiff
path: root/simpleperf/scripts/report.py
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2017-01-05 17:58:19 -0800
committerYabin Cui <yabinc@google.com>2017-01-06 09:52:32 -0800
commit182d8a03949aca735fd95953a3efad20068b1bad (patch)
tree678535ed467bcbe4cab40836b44b282f5bb5a5a7 /simpleperf/scripts/report.py
parent8d367acde0d5fd09a3427703dc11583d44eb8199 (diff)
downloadextras-182d8a03949aca735fd95953a3efad20068b1bad.tar.gz
simpleperf: add scripts to sdk build.
Bug: http://b/32834638 Test: make sdk. Change-Id: Iffc2649dc131c73758db8727912c83c078d92071
Diffstat (limited to 'simpleperf/scripts/report.py')
-rw-r--r--simpleperf/scripts/report.py262
1 files changed, 262 insertions, 0 deletions
diff --git a/simpleperf/scripts/report.py b/simpleperf/scripts/report.py
new file mode 100644
index 00000000..b58ac6bc
--- /dev/null
+++ b/simpleperf/scripts/report.py
@@ -0,0 +1,262 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""Simpleperf gui reporter: provide gui interface for simpleperf report command.
+
+There are two ways to use gui reporter. One way is to pass it a report file
+generated by simpleperf report command, and reporter will display it. The
+other ways is to pass it any arguments you want to use when calling
+simpleperf report command. The reporter will call `simpleperf report` to
+generate report file, and display it.
+"""
+
+import os.path
+import re
+import subprocess
+import sys
+from tkFont import *
+from Tkinter import *
+from ttk import *
+from utils import *
+
+PAD_X = 3
+PAD_Y = 3
+
+
+class CallTreeNode(object):
+
+ """Representing a node in call-graph."""
+
+ def __init__(self, percentage, function_name):
+ self.percentage = percentage
+ self.call_stack = [function_name]
+ self.children = []
+
+ def add_call(self, function_name):
+ self.call_stack.append(function_name)
+
+ def add_child(self, node):
+ self.children.append(node)
+
+ def __str__(self):
+ strs = self.dump()
+ return '\n'.join(strs)
+
+ def dump(self):
+ strs = []
+ strs.append('CallTreeNode percentage = %.2f' % self.percentage)
+ for function_name in self.call_stack:
+ strs.append(' %s' % function_name)
+ for child in self.children:
+ child_strs = child.dump()
+ strs.extend([' ' + x for x in child_strs])
+ return strs
+
+
+class ReportItem(object):
+
+ """Representing one item in report, may contain a CallTree."""
+
+ def __init__(self, raw_line):
+ self.raw_line = raw_line
+ self.call_tree = None
+
+ def __str__(self):
+ strs = []
+ strs.append('ReportItem (raw_line %s)' % self.raw_line)
+ if self.call_tree is not None:
+ strs.append('%s' % self.call_tree)
+ return '\n'.join(strs)
+
+
+def parse_report_items(lines):
+ report_items = []
+ cur_report_item = None
+ call_tree_stack = {}
+ vertical_columns = []
+ last_node = None
+
+ for line in lines:
+ if not line:
+ continue
+ if not line[0].isspace():
+ cur_report_item = ReportItem(line)
+ report_items.append(cur_report_item)
+ # Each report item can have different column depths.
+ vertical_columns = []
+ else:
+ for i in range(len(line)):
+ if line[i] == '|':
+ if not vertical_columns or vertical_columns[-1] < i:
+ vertical_columns.append(i)
+
+ if not line.strip('| \t'):
+ continue
+ if line.find('-') == -1:
+ line = line.strip('| \t')
+ function_name = line
+ last_node.add_call(function_name)
+ else:
+ pos = line.find('-')
+ depth = -1
+ for i in range(len(vertical_columns)):
+ if pos >= vertical_columns[i]:
+ depth = i
+ assert depth != -1
+
+ line = line.strip('|- \t')
+ m = re.search(r'^([\d\.]+)%[-\s]+(.+)$', line)
+ if m:
+ percentage = float(m.group(1))
+ function_name = m.group(2)
+ else:
+ percentage = 100.0
+ function_name = line
+
+ node = CallTreeNode(percentage, function_name)
+ if depth == 0:
+ cur_report_item.call_tree = node
+ else:
+ call_tree_stack[depth - 1].add_child(node)
+ call_tree_stack[depth] = node
+ last_node = node
+
+ return report_items
+
+
+class ReportWindow(object):
+
+ """A window used to display report file."""
+
+ def __init__(self, master, report_context, title_line, report_items):
+ frame = Frame(master)
+ frame.pack(fill=BOTH, expand=1)
+
+ font = Font(family='courier', size=10)
+
+ # Report Context
+ for line in report_context:
+ label = Label(frame, text=line, font=font)
+ label.pack(anchor=W, padx=PAD_X, pady=PAD_Y)
+
+ # Space
+ label = Label(frame, text='', font=font)
+ label.pack(anchor=W, padx=PAD_X, pady=PAD_Y)
+
+ # Title
+ label = Label(frame, text=' ' + title_line, font=font)
+ label.pack(anchor=W, padx=PAD_X, pady=PAD_Y)
+
+ # Report Items
+ report_frame = Frame(frame)
+ report_frame.pack(fill=BOTH, expand=1)
+
+ yscrollbar = Scrollbar(report_frame)
+ yscrollbar.pack(side=RIGHT, fill=Y)
+ xscrollbar = Scrollbar(report_frame, orient=HORIZONTAL)
+ xscrollbar.pack(side=BOTTOM, fill=X)
+
+ tree = Treeview(report_frame, columns=[title_line], show='')
+ tree.pack(side=LEFT, fill=BOTH, expand=1)
+ tree.tag_configure('set_font', font=font)
+
+ tree.config(yscrollcommand=yscrollbar.set)
+ yscrollbar.config(command=tree.yview)
+ tree.config(xscrollcommand=xscrollbar.set)
+ xscrollbar.config(command=tree.xview)
+
+ self.display_report_items(tree, report_items)
+
+ def display_report_items(self, tree, report_items):
+ for report_item in report_items:
+ prefix_str = '+ ' if report_item.call_tree is not None else ' '
+ id = tree.insert(
+ '',
+ 'end',
+ None,
+ values=[
+ prefix_str +
+ report_item.raw_line],
+ tag='set_font')
+ if report_item.call_tree is not None:
+ self.display_call_tree(tree, id, report_item.call_tree, 1)
+
+ def display_call_tree(self, tree, parent_id, node, indent):
+ id = parent_id
+ indent_str = ' ' * indent
+
+ if node.percentage != 100.0:
+ percentage_str = '%.2f%%' % node.percentage
+ else:
+ percentage_str = ''
+ first_open = True if node.percentage == 100.0 else False
+
+ for i in range(len(node.call_stack)):
+ s = indent_str
+ s += '+ ' if node.children else ' '
+ s += percentage_str if i == 0 else ' ' * len(percentage_str)
+ s += node.call_stack[i]
+ child_open = first_open if i == 0 else True
+ id = tree.insert(id, 'end', None, values=[s], open=child_open,
+ tag='set_font')
+
+ for child in node.children:
+ self.display_call_tree(tree, id, child, indent + 1)
+
+
+def display_report_file(report_file):
+ fh = open(report_file, 'r')
+ lines = fh.readlines()
+ fh.close()
+
+ lines = [x.rstrip() for x in lines]
+
+ blank_line_index = -1
+ for i in range(len(lines)):
+ if not lines[i]:
+ blank_line_index = i
+ break
+ assert blank_line_index != -1
+ assert blank_line_index + 1 < len(lines)
+
+ report_context = lines[:blank_line_index]
+ title_line = lines[blank_line_index + 1]
+ report_items = parse_report_items(lines[blank_line_index + 2:])
+
+ root = Tk()
+ ReportWindow(root, report_context, title_line, report_items)
+ root.mainloop()
+
+
+def call_simpleperf_report(args, report_file):
+ output_fh = open(report_file, 'w')
+ simpleperf_path = get_host_binary_path('simpleperf')
+ args = [simpleperf_path, 'report'] + args
+ subprocess.check_call(args, stdout=output_fh)
+ output_fh.close()
+
+
+def main():
+ if len(sys.argv) == 2 and os.path.isfile(sys.argv[1]):
+ display_report_file(sys.argv[1])
+ else:
+ call_simpleperf_report(sys.argv[1:], 'perf.report')
+ display_report_file('perf.report')
+
+
+if __name__ == '__main__':
+ main()