diff options
Diffstat (limited to 'heatmaps/heat_map.py')
-rwxr-xr-x | heatmaps/heat_map.py | 341 |
1 files changed, 181 insertions, 160 deletions
diff --git a/heatmaps/heat_map.py b/heatmaps/heat_map.py index a989ab70..a3c52369 100755 --- a/heatmaps/heat_map.py +++ b/heatmaps/heat_map.py @@ -1,12 +1,11 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2015 The Chromium OS Authors. All rights reserved. +# Copyright 2015 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Wrapper to generate heat maps for chrome.""" -from __future__ import print_function import argparse import os @@ -19,167 +18,189 @@ from heatmaps import heatmap_generator def IsARepoRoot(directory): - """Returns True if directory is the root of a repo checkout.""" - return os.path.exists( - os.path.join(os.path.realpath(os.path.expanduser(directory)), '.repo')) + """Returns True if directory is the root of a repo checkout.""" + return os.path.exists( + os.path.join(os.path.realpath(os.path.expanduser(directory)), ".repo") + ) class HeatMapProducer(object): - """Class to produce heat map.""" - - def __init__(self, - chromeos_root, - perf_data, - hugepage, - binary, - title, - logger=None): - self.chromeos_root = os.path.realpath(os.path.expanduser(chromeos_root)) - self.perf_data = os.path.realpath(os.path.expanduser(perf_data)) - self.hugepage = hugepage - self.dir = os.path.dirname(os.path.realpath(__file__)) - self.binary = binary - self.ce = command_executer.GetCommandExecuter() - self.temp_dir = '' - self.temp_perf_inchroot = '' - self.temp_dir_created = False - self.perf_report = '' - self.title = title - self.logger = logger - - def _EnsureFileInChroot(self): - chroot_prefix = os.path.join(self.chromeos_root, 'chroot') - if self.perf_data.startswith(chroot_prefix): - # If the path to perf_data starts with the same chromeos_root, assume - # it's in the chromeos_root so no need for temporary directory and copy. - self.temp_dir = self.perf_data.replace('perf.data', '') - self.temp_perf_inchroot = self.temp_dir.replace(chroot_prefix, '') - - else: - # Otherwise, create a temporary directory and copy perf.data into chroot. - self.temp_dir = tempfile.mkdtemp( - prefix=os.path.join(self.chromeos_root, 'src/')) - temp_perf = os.path.join(self.temp_dir, 'perf.data') - shutil.copy2(self.perf_data, temp_perf) - self.temp_perf_inchroot = os.path.join('~/trunk/src', - os.path.basename(self.temp_dir)) - self.temp_dir_created = True - - def _GeneratePerfReport(self): - cmd = ('cd %s && perf report -D -i perf.data > perf_report.txt' % - self.temp_perf_inchroot) - retval = self.ce.ChrootRunCommand(self.chromeos_root, cmd) - if retval: - raise RuntimeError('Failed to generate perf report') - self.perf_report = os.path.join(self.temp_dir, 'perf_report.txt') - - def _GetHeatMap(self, top_n_pages): - generator = heatmap_generator.HeatmapGenerator( - perf_report=self.perf_report, - page_size=4096, - hugepage=self.hugepage, - title=self.title) - generator.draw() - # Analyze top N hottest symbols with the binary, if provided - if self.binary: - generator.analyze(self.binary, top_n_pages) - - def _RemoveFiles(self): - files = [ - 'out.txt', 'inst-histo.txt', 'inst-histo-hp.txt', 'inst-histo-sp.txt' - ] - for f in files: - if os.path.exists(f): - os.remove(f) - - def Run(self, top_n_pages): - try: - self._EnsureFileInChroot() - self._GeneratePerfReport() - self._GetHeatMap(top_n_pages) - finally: - self._RemoveFiles() - msg = ('heat map and time histogram genereated in the current ' - 'directory with name heat_map.png and timeline.png ' - 'accordingly.') - if self.binary: - msg += ('\nThe hottest %d pages inside and outside hugepage ' - 'is symbolized and saved to addr2symbol.txt' % top_n_pages) - if self.logger: - self.logger.LogOutput(msg) - else: - print(msg) + """Class to produce heat map.""" + + def __init__( + self, chromeos_root, perf_data, hugepage, binary, title, logger=None + ): + self.chromeos_root = os.path.realpath(os.path.expanduser(chromeos_root)) + self.perf_data = os.path.realpath(os.path.expanduser(perf_data)) + self.hugepage = hugepage + self.dir = os.path.dirname(os.path.realpath(__file__)) + self.binary = binary + self.ce = command_executer.GetCommandExecuter() + self.temp_dir = "" + self.temp_perf_inchroot = "" + self.temp_dir_created = False + self.perf_report = "" + self.title = title + self.logger = logger + + def _EnsureFileInChroot(self): + chroot_prefix = os.path.join(self.chromeos_root, "chroot") + if self.perf_data.startswith(chroot_prefix): + # If the path to perf_data starts with the same chromeos_root, assume + # it's in the chromeos_root so no need for temporary directory and copy. + self.temp_dir = self.perf_data.replace("perf.data", "") + self.temp_perf_inchroot = self.temp_dir.replace(chroot_prefix, "") + + else: + # Otherwise, create a temporary directory and copy perf.data into chroot. + self.temp_dir = tempfile.mkdtemp( + prefix=os.path.join(self.chromeos_root, "src/") + ) + temp_perf = os.path.join(self.temp_dir, "perf.data") + shutil.copy2(self.perf_data, temp_perf) + self.temp_perf_inchroot = os.path.join( + "~/trunk/src", os.path.basename(self.temp_dir) + ) + self.temp_dir_created = True + + def _GeneratePerfReport(self): + cmd = ( + "cd %s && perf report -D -i perf.data > perf_report.txt" + % self.temp_perf_inchroot + ) + retval = self.ce.ChrootRunCommand(self.chromeos_root, cmd) + if retval: + raise RuntimeError("Failed to generate perf report") + self.perf_report = os.path.join(self.temp_dir, "perf_report.txt") + + def _GetHeatMap(self, top_n_pages): + generator = heatmap_generator.HeatmapGenerator( + perf_report=self.perf_report, + page_size=4096, + hugepage=self.hugepage, + title=self.title, + ) + generator.draw() + # Analyze top N hottest symbols with the binary, if provided + if self.binary: + generator.analyze(self.binary, top_n_pages) + + def _RemoveFiles(self): + files = [ + "out.txt", + "inst-histo.txt", + "inst-histo-hp.txt", + "inst-histo-sp.txt", + ] + for f in files: + if os.path.exists(f): + os.remove(f) + + def Run(self, top_n_pages): + try: + self._EnsureFileInChroot() + self._GeneratePerfReport() + self._GetHeatMap(top_n_pages) + finally: + self._RemoveFiles() + msg = ( + "heat map and time histogram genereated in the current " + "directory with name heat_map.png and timeline.png " + "accordingly." + ) + if self.binary: + msg += ( + "\nThe hottest %d pages inside and outside hugepage " + "is symbolized and saved to addr2symbol.txt" % top_n_pages + ) + if self.logger: + self.logger.LogOutput(msg) + else: + print(msg) def main(argv): - """Parse the options. - - Args: - argv: The options with which this script was invoked. - - Returns: - 0 unless an exception is raised. - """ - parser = argparse.ArgumentParser() - - parser.add_argument( - '--chromeos_root', - dest='chromeos_root', - required=True, - help='ChromeOS root to use for generate heatmaps.') - parser.add_argument( - '--perf_data', - dest='perf_data', - required=True, - help='The raw perf data. Must be collected with -e instructions while ' - 'disabling ASLR.') - parser.add_argument( - '--binary', - dest='binary', - help='The path to the Chrome binary. Only useful if want to print ' - 'symbols on hottest pages', - default=None) - parser.add_argument( - '--top_n', - dest='top_n', - type=int, - default=10, - help='Print out top N hottest pages within/outside huge page range. ' - 'Must be used with --hugepage and --binary. (Default: %(default)s)') - parser.add_argument( - '--title', dest='title', help='Title of the heatmap', default='') - parser.add_argument( - '--hugepage', - dest='hugepage', - help='A range of addresses (start,end) where huge page starts and ends' - ' in text section, separated by a comma.' - ' Used to differentiate regions in heatmap.' - ' Example: --hugepage=0,4096' - ' If not specified, no effect on the heatmap.', - default=None) - - options = parser.parse_args(argv) - - if not IsARepoRoot(options.chromeos_root): - parser.error('%s does not contain .repo dir.' % options.chromeos_root) - - if not os.path.isfile(options.perf_data): - parser.error('Cannot find perf_data: %s.' % options.perf_data) - - hugepage_range = None - if options.hugepage: - hugepage_range = options.hugepage.split(',') - if len(hugepage_range) != 2 or \ - int(hugepage_range[0]) > int(hugepage_range[1]): - parser.error('Wrong format of hugepage range: %s' % options.hugepage) - hugepage_range = [int(x) for x in hugepage_range] - - heatmap_producer = HeatMapProducer(options.chromeos_root, options.perf_data, - hugepage_range, options.binary, - options.title) - - heatmap_producer.Run(options.top_n) - - -if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + """Parse the options. + + Args: + argv: The options with which this script was invoked. + + Returns: + 0 unless an exception is raised. + """ + parser = argparse.ArgumentParser() + + parser.add_argument( + "--chromeos_root", + dest="chromeos_root", + required=True, + help="ChromeOS root to use for generate heatmaps.", + ) + parser.add_argument( + "--perf_data", + dest="perf_data", + required=True, + help="The raw perf data. Must be collected with -e instructions while " + "disabling ASLR.", + ) + parser.add_argument( + "--binary", + dest="binary", + help="The path to the Chrome binary. Only useful if want to print " + "symbols on hottest pages", + default=None, + ) + parser.add_argument( + "--top_n", + dest="top_n", + type=int, + default=10, + help="Print out top N hottest pages within/outside huge page range. " + "Must be used with --hugepage and --binary. (Default: %(default)s)", + ) + parser.add_argument( + "--title", dest="title", help="Title of the heatmap", default="" + ) + parser.add_argument( + "--hugepage", + dest="hugepage", + help="A range of addresses (start,end) where huge page starts and ends" + " in text section, separated by a comma." + " Used to differentiate regions in heatmap." + " Example: --hugepage=0,4096" + " If not specified, no effect on the heatmap.", + default=None, + ) + + options = parser.parse_args(argv) + + if not IsARepoRoot(options.chromeos_root): + parser.error("%s does not contain .repo dir." % options.chromeos_root) + + if not os.path.isfile(options.perf_data): + parser.error("Cannot find perf_data: %s." % options.perf_data) + + hugepage_range = None + if options.hugepage: + hugepage_range = options.hugepage.split(",") + if len(hugepage_range) != 2 or int(hugepage_range[0]) > int( + hugepage_range[1] + ): + parser.error( + "Wrong format of hugepage range: %s" % options.hugepage + ) + hugepage_range = [int(x) for x in hugepage_range] + + heatmap_producer = HeatMapProducer( + options.chromeos_root, + options.perf_data, + hugepage_range, + options.binary, + options.title, + ) + + heatmap_producer.Run(options.top_n) + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) |