aboutsummaryrefslogtreecommitdiff
path: root/cros_utils/perf_diff.py
diff options
context:
space:
mode:
Diffstat (limited to 'cros_utils/perf_diff.py')
-rwxr-xr-xcros_utils/perf_diff.py590
1 files changed, 304 insertions, 286 deletions
diff --git a/cros_utils/perf_diff.py b/cros_utils/perf_diff.py
index b8ddb0c4..6647b76a 100755
--- a/cros_utils/perf_diff.py
+++ b/cros_utils/perf_diff.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Copyright 2019 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -9,9 +9,8 @@
A detailed description of perf_diff.
"""
-from __future__ import print_function
-__author__ = 'asharif@google.com (Ahmad Sharif)'
+__author__ = "asharif@google.com (Ahmad Sharif)"
import argparse
import functools
@@ -21,319 +20,338 @@ import sys
from cros_utils import misc
from cros_utils import tabulator
-ROWS_TO_SHOW = 'Rows_to_show_in_the_perf_table'
-TOTAL_EVENTS = 'Total_events_of_this_profile'
+
+ROWS_TO_SHOW = "Rows_to_show_in_the_perf_table"
+TOTAL_EVENTS = "Total_events_of_this_profile"
def GetPerfDictFromReport(report_file):
- output = {}
- perf_report = PerfReport(report_file)
- for k, v in perf_report.sections.items():
- if k not in output:
- output[k] = {}
- output[k][ROWS_TO_SHOW] = 0
- output[k][TOTAL_EVENTS] = 0
- for function in v.functions:
- out_key = '%s' % (function.name)
- output[k][out_key] = function.count
- output[k][TOTAL_EVENTS] += function.count
- if function.percent > 1:
- output[k][ROWS_TO_SHOW] += 1
- return output
+ output = {}
+ perf_report = PerfReport(report_file)
+ for k, v in perf_report.sections.items():
+ if k not in output:
+ output[k] = {}
+ output[k][ROWS_TO_SHOW] = 0
+ output[k][TOTAL_EVENTS] = 0
+ for function in v.functions:
+ out_key = "%s" % (function.name)
+ output[k][out_key] = function.count
+ output[k][TOTAL_EVENTS] += function.count
+ if function.percent > 1:
+ output[k][ROWS_TO_SHOW] += 1
+ return output
def _SortDictionaryByValue(d):
- l = d.items()
+ l = d.items()
- def GetFloat(x):
- if misc.IsFloat(x):
- return float(x)
- else:
- return x
+ def GetFloat(x):
+ if misc.IsFloat(x):
+ return float(x)
+ else:
+ return x
- sorted_l = sorted(l, key=lambda x: GetFloat(x[1]))
- sorted_l.reverse()
- return [f[0] for f in sorted_l]
+ sorted_l = sorted(l, key=lambda x: GetFloat(x[1]))
+ sorted_l.reverse()
+ return [f[0] for f in sorted_l]
class Tabulator(object):
- """Make tables."""
-
- def __init__(self, all_dicts):
- self._all_dicts = all_dicts
-
- def PrintTable(self):
- for dicts in self._all_dicts:
- self.PrintTableHelper(dicts)
-
- def PrintTableHelper(self, dicts):
- """Transfrom dicts to tables."""
- fields = {}
- for d in dicts:
- for f in d.keys():
- if f not in fields:
- fields[f] = d[f]
- else:
- fields[f] = max(fields[f], d[f])
- table = []
- header = ['name']
- for i in range(len(dicts)):
- header.append(i)
+ """Make tables."""
- table.append(header)
+ def __init__(self, all_dicts):
+ self._all_dicts = all_dicts
- sorted_fields = _SortDictionaryByValue(fields)
+ def PrintTable(self):
+ for dicts in self._all_dicts:
+ self.PrintTableHelper(dicts)
- for f in sorted_fields:
- row = [f]
- for d in dicts:
- if f in d:
- row.append(d[f])
- else:
- row.append('0')
- table.append(row)
+ def PrintTableHelper(self, dicts):
+ """Transfrom dicts to tables."""
+ fields = {}
+ for d in dicts:
+ for f in d.keys():
+ if f not in fields:
+ fields[f] = d[f]
+ else:
+ fields[f] = max(fields[f], d[f])
+ table = []
+ header = ["name"]
+ for i in range(len(dicts)):
+ header.append(i)
+
+ table.append(header)
- print(tabulator.GetSimpleTable(table))
+ sorted_fields = _SortDictionaryByValue(fields)
+
+ for f in sorted_fields:
+ row = [f]
+ for d in dicts:
+ if f in d:
+ row.append(d[f])
+ else:
+ row.append("0")
+ table.append(row)
+
+ print(tabulator.GetSimpleTable(table))
class Function(object):
- """Function for formatting."""
+ """Function for formatting."""
- def __init__(self):
- self.count = 0
- self.name = ''
- self.percent = 0
+ def __init__(self):
+ self.count = 0
+ self.name = ""
+ self.percent = 0
class Section(object):
- """Section formatting."""
-
- def __init__(self, contents):
- self.name = ''
- self.raw_contents = contents
- self._ParseSection()
-
- def _ParseSection(self):
- matches = re.findall(r'Events: (\w+)\s+(.*)', self.raw_contents)
- assert len(matches) <= 1, 'More than one event found in 1 section'
- if not matches:
- return
- match = matches[0]
- self.name = match[1]
- self.count = misc.UnitToNumber(match[0])
-
- self.functions = []
- for line in self.raw_contents.splitlines():
- if not line.strip():
- continue
- if '%' not in line:
- continue
- if not line.startswith('#'):
- fields = [f for f in line.split(' ') if f]
- function = Function()
- function.percent = float(fields[0].strip('%'))
- function.count = int(fields[1])
- function.name = ' '.join(fields[2:])
- self.functions.append(function)
+ """Section formatting."""
+
+ def __init__(self, contents):
+ self.name = ""
+ self.raw_contents = contents
+ self._ParseSection()
+
+ def _ParseSection(self):
+ matches = re.findall(r"Events: (\w+)\s+(.*)", self.raw_contents)
+ assert len(matches) <= 1, "More than one event found in 1 section"
+ if not matches:
+ return
+ match = matches[0]
+ self.name = match[1]
+ self.count = misc.UnitToNumber(match[0])
+
+ self.functions = []
+ for line in self.raw_contents.splitlines():
+ if not line.strip():
+ continue
+ if "%" not in line:
+ continue
+ if not line.startswith("#"):
+ fields = [f for f in line.split(" ") if f]
+ function = Function()
+ function.percent = float(fields[0].strip("%"))
+ function.count = int(fields[1])
+ function.name = " ".join(fields[2:])
+ self.functions.append(function)
class PerfReport(object):
- """Get report from raw report."""
-
- def __init__(self, perf_file):
- self.perf_file = perf_file
- self._ReadFile()
- self.sections = {}
- self.metadata = {}
- self._section_contents = []
- self._section_header = ''
- self._SplitSections()
- self._ParseSections()
- self._ParseSectionHeader()
-
- def _ParseSectionHeader(self):
- """Parse a header of a perf report file."""
- # The "captured on" field is inaccurate - this actually refers to when the
- # report was generated, not when the data was captured.
- for line in self._section_header.splitlines():
- line = line[2:]
- if ':' in line:
- key, val = line.strip().split(':', 1)
- key = key.strip()
- val = val.strip()
- self.metadata[key] = val
-
- def _ReadFile(self):
- self._perf_contents = open(self.perf_file).read()
-
- def _ParseSections(self):
- self.event_counts = {}
- self.sections = {}
- for section_content in self._section_contents:
- section = Section(section_content)
- section.name = self._GetHumanReadableName(section.name)
- self.sections[section.name] = section
-
- # TODO(asharif): Do this better.
- def _GetHumanReadableName(self, section_name):
- if not 'raw' in section_name:
- return section_name
- raw_number = section_name.strip().split(' ')[-1]
- for line in self._section_header.splitlines():
- if raw_number in line:
- name = line.strip().split(' ')[5]
- return name
-
- def _SplitSections(self):
- self._section_contents = []
- indices = [m.start() for m in re.finditer('# Events:', self._perf_contents)]
- indices.append(len(self._perf_contents))
- for i in range(len(indices) - 1):
- section_content = self._perf_contents[indices[i]:indices[i + 1]]
- self._section_contents.append(section_content)
- self._section_header = ''
- if indices:
- self._section_header = self._perf_contents[0:indices[0]]
+ """Get report from raw report."""
+
+ def __init__(self, perf_file):
+ self.perf_file = perf_file
+ self._ReadFile()
+ self.sections = {}
+ self.metadata = {}
+ self._section_contents = []
+ self._section_header = ""
+ self._SplitSections()
+ self._ParseSections()
+ self._ParseSectionHeader()
+
+ def _ParseSectionHeader(self):
+ """Parse a header of a perf report file."""
+ # The "captured on" field is inaccurate - this actually refers to when the
+ # report was generated, not when the data was captured.
+ for line in self._section_header.splitlines():
+ line = line[2:]
+ if ":" in line:
+ key, val = line.strip().split(":", 1)
+ key = key.strip()
+ val = val.strip()
+ self.metadata[key] = val
+
+ def _ReadFile(self):
+ self._perf_contents = open(self.perf_file).read()
+
+ def _ParseSections(self):
+ self.event_counts = {}
+ self.sections = {}
+ for section_content in self._section_contents:
+ section = Section(section_content)
+ section.name = self._GetHumanReadableName(section.name)
+ self.sections[section.name] = section
+
+ # TODO(asharif): Do this better.
+ def _GetHumanReadableName(self, section_name):
+ if not "raw" in section_name:
+ return section_name
+ raw_number = section_name.strip().split(" ")[-1]
+ for line in self._section_header.splitlines():
+ if raw_number in line:
+ name = line.strip().split(" ")[5]
+ return name
+
+ def _SplitSections(self):
+ self._section_contents = []
+ indices = [
+ m.start() for m in re.finditer("# Events:", self._perf_contents)
+ ]
+ indices.append(len(self._perf_contents))
+ for i in range(len(indices) - 1):
+ section_content = self._perf_contents[indices[i] : indices[i + 1]]
+ self._section_contents.append(section_content)
+ self._section_header = ""
+ if indices:
+ self._section_header = self._perf_contents[0 : indices[0]]
class PerfDiffer(object):
- """Perf differ class."""
-
- def __init__(self, reports, num_symbols, common_only):
- self._reports = reports
- self._num_symbols = num_symbols
- self._common_only = common_only
- self._common_function_names = {}
-
- def DoDiff(self):
- """The function that does the diff."""
- section_names = self._FindAllSections()
-
- filename_dicts = []
- summary_dicts = []
- for report in self._reports:
- d = {}
- filename_dicts.append({'file': report.perf_file})
- for section_name in section_names:
- if section_name in report.sections:
- d[section_name] = report.sections[section_name].count
- summary_dicts.append(d)
-
- all_dicts = [filename_dicts, summary_dicts]
-
- for section_name in section_names:
- function_names = self._GetTopFunctions(section_name, self._num_symbols)
- self._FindCommonFunctions(section_name)
- dicts = []
- for report in self._reports:
+ """Perf differ class."""
+
+ def __init__(self, reports, num_symbols, common_only):
+ self._reports = reports
+ self._num_symbols = num_symbols
+ self._common_only = common_only
+ self._common_function_names = {}
+
+ def DoDiff(self):
+ """The function that does the diff."""
+ section_names = self._FindAllSections()
+
+ filename_dicts = []
+ summary_dicts = []
+ for report in self._reports:
+ d = {}
+ filename_dicts.append({"file": report.perf_file})
+ for section_name in section_names:
+ if section_name in report.sections:
+ d[section_name] = report.sections[section_name].count
+ summary_dicts.append(d)
+
+ all_dicts = [filename_dicts, summary_dicts]
+
+ for section_name in section_names:
+ function_names = self._GetTopFunctions(
+ section_name, self._num_symbols
+ )
+ self._FindCommonFunctions(section_name)
+ dicts = []
+ for report in self._reports:
+ d = {}
+ if section_name in report.sections:
+ section = report.sections[section_name]
+
+ # Get a common scaling factor for this report.
+ common_scaling_factor = self._GetCommonScalingFactor(
+ section
+ )
+
+ for function in section.functions:
+ if function.name in function_names:
+ key = "%s %s" % (section.name, function.name)
+ d[key] = function.count
+ # Compute a factor to scale the function count by in common_only
+ # mode.
+ if self._common_only and (
+ function.name
+ in self._common_function_names[section.name]
+ ):
+ d[key + " scaled"] = (
+ common_scaling_factor * function.count
+ )
+ dicts.append(d)
+
+ all_dicts.append(dicts)
+
+ mytabulator = Tabulator(all_dicts)
+ mytabulator.PrintTable()
+
+ def _FindAllSections(self):
+ sections = {}
+ for report in self._reports:
+ for section in report.sections.values():
+ if section.name not in sections:
+ sections[section.name] = section.count
+ else:
+ sections[section.name] = max(
+ sections[section.name], section.count
+ )
+ return _SortDictionaryByValue(sections)
+
+ def _GetCommonScalingFactor(self, section):
+ unique_count = self._GetCount(
+ section, lambda x: x in self._common_function_names[section.name]
+ )
+ return 100.0 / unique_count
+
+ def _GetCount(self, section, filter_fun=None):
+ total_count = 0
+ for function in section.functions:
+ if not filter_fun or filter_fun(function.name):
+ total_count += int(function.count)
+ return total_count
+
+ def _FindCommonFunctions(self, section_name):
+ function_names_list = []
+ for report in self._reports:
+ if section_name in report.sections:
+ section = report.sections[section_name]
+ function_names = {f.name for f in section.functions}
+ function_names_list.append(function_names)
+
+ self._common_function_names[section_name] = functools.reduce(
+ set.intersection, function_names_list
+ )
+
+ def _GetTopFunctions(self, section_name, num_functions):
+ all_functions = {}
+ for report in self._reports:
+ if section_name in report.sections:
+ section = report.sections[section_name]
+ for f in section.functions[:num_functions]:
+ if f.name in all_functions:
+ all_functions[f.name] = max(
+ all_functions[f.name], f.count
+ )
+ else:
+ all_functions[f.name] = f.count
+ # FIXME(asharif): Don't really need to sort these...
+ return _SortDictionaryByValue(all_functions)
+
+ def _GetFunctionsDict(self, section, function_names):
d = {}
- if section_name in report.sections:
- section = report.sections[section_name]
-
- # Get a common scaling factor for this report.
- common_scaling_factor = self._GetCommonScalingFactor(section)
-
- for function in section.functions:
+ for function in section.functions:
if function.name in function_names:
- key = '%s %s' % (section.name, function.name)
- d[key] = function.count
- # Compute a factor to scale the function count by in common_only
- # mode.
- if self._common_only and (
- function.name in self._common_function_names[section.name]):
- d[key + ' scaled'] = common_scaling_factor * function.count
- dicts.append(d)
-
- all_dicts.append(dicts)
-
- mytabulator = Tabulator(all_dicts)
- mytabulator.PrintTable()
-
- def _FindAllSections(self):
- sections = {}
- for report in self._reports:
- for section in report.sections.values():
- if section.name not in sections:
- sections[section.name] = section.count
- else:
- sections[section.name] = max(sections[section.name], section.count)
- return _SortDictionaryByValue(sections)
-
- def _GetCommonScalingFactor(self, section):
- unique_count = self._GetCount(
- section, lambda x: x in self._common_function_names[section.name])
- return 100.0 / unique_count
-
- def _GetCount(self, section, filter_fun=None):
- total_count = 0
- for function in section.functions:
- if not filter_fun or filter_fun(function.name):
- total_count += int(function.count)
- return total_count
-
- def _FindCommonFunctions(self, section_name):
- function_names_list = []
- for report in self._reports:
- if section_name in report.sections:
- section = report.sections[section_name]
- function_names = {f.name for f in section.functions}
- function_names_list.append(function_names)
-
- self._common_function_names[section_name] = (
- functools.reduce(set.intersection, function_names_list))
-
- def _GetTopFunctions(self, section_name, num_functions):
- all_functions = {}
- for report in self._reports:
- if section_name in report.sections:
- section = report.sections[section_name]
- for f in section.functions[:num_functions]:
- if f.name in all_functions:
- all_functions[f.name] = max(all_functions[f.name], f.count)
- else:
- all_functions[f.name] = f.count
- # FIXME(asharif): Don't really need to sort these...
- return _SortDictionaryByValue(all_functions)
-
- def _GetFunctionsDict(self, section, function_names):
- d = {}
- for function in section.functions:
- if function.name in function_names:
- d[function.name] = function.count
- return d
+ d[function.name] = function.count
+ return d
def Main(argv):
- """The entry of the main."""
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '-n',
- '--num_symbols',
- dest='num_symbols',
- default='5',
- help='The number of symbols to show.')
- parser.add_argument(
- '-c',
- '--common_only',
- dest='common_only',
- action='store_true',
- default=False,
- help='Diff common symbols only.')
-
- options, args = parser.parse_known_args(argv)
-
- try:
- reports = []
- for report in args[1:]:
- report = PerfReport(report)
- reports.append(report)
- pd = PerfDiffer(reports, int(options.num_symbols), options.common_only)
- pd.DoDiff()
- finally:
- pass
-
- return 0
-
-
-if __name__ == '__main__':
- sys.exit(Main(sys.argv))
+ """The entry of the main."""
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-n",
+ "--num_symbols",
+ dest="num_symbols",
+ default="5",
+ help="The number of symbols to show.",
+ )
+ parser.add_argument(
+ "-c",
+ "--common_only",
+ dest="common_only",
+ action="store_true",
+ default=False,
+ help="Diff common symbols only.",
+ )
+
+ options, args = parser.parse_known_args(argv)
+
+ try:
+ reports = []
+ for report in args[1:]:
+ report = PerfReport(report)
+ reports.append(report)
+ pd = PerfDiffer(reports, int(options.num_symbols), options.common_only)
+ pd.DoDiff()
+ finally:
+ pass
+
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(Main(sys.argv))