1#!/usr/bin/env python2 2# Copyright 2015 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5"""Wrapper to generate heat maps for chrome.""" 6 7from __future__ import print_function 8 9import argparse 10import shutil 11import os 12import sys 13import tempfile 14from sets import Set 15 16from cros_utils import command_executer 17 18 19def IsARepoRoot(directory): 20 """Returns True if directory is the root of a repo checkout.""" 21 return os.path.exists(os.path.join(directory, '.repo')) 22 23 24class HeatMapProducer(object): 25 """Class to produce heat map.""" 26 27 def __init__(self, chromeos_root, perf_data, page_size, binary): 28 self.chromeos_root = os.path.realpath(chromeos_root) 29 self.perf_data = os.path.realpath(perf_data) 30 self.page_size = page_size 31 self.dir = os.path.dirname(os.path.realpath(__file__)) 32 self.binary = binary 33 self.tempDir = '' 34 self.ce = command_executer.GetCommandExecuter() 35 self.loading_address = None 36 self.temp_perf = '' 37 self.temp_perf_inchroot = '' 38 self.perf_report = '' 39 40 def copyFileToChroot(self): 41 self.tempDir = tempfile.mkdtemp(prefix=os.path.join(self.chromeos_root, 42 'src/')) 43 self.temp_perf = os.path.join(self.tempDir, 'perf.data') 44 shutil.copy2(self.perf_data, self.temp_perf) 45 self.temp_perf_inchroot = os.path.join('~/trunk/src', 46 os.path.basename(self.tempDir)) 47 48 def getPerfReport(self): 49 cmd = ('cd %s; perf report -D -i perf.data > perf_report.txt' % 50 self.temp_perf_inchroot) 51 retval = self.ce.ChrootRunCommand(self.chromeos_root, cmd) 52 if retval: 53 raise RuntimeError('Failed to generate perf report') 54 self.perf_report = os.path.join(self.tempDir, 'perf_report.txt') 55 56 def getBinaryBaseAddress(self): 57 cmd = 'grep PERF_RECORD_MMAP %s | grep "%s$"' % (self.perf_report, 58 self.binary) 59 retval, output, _ = self.ce.RunCommandWOutput(cmd) 60 if retval: 61 raise RuntimeError('Failed to run grep to get base address') 62 baseAddresses = Set() 63 for line in output.strip().split('\n'): 64 head = line.split('[')[2] 65 address = head.split('(')[0] 66 baseAddresses.add(address) 67 if len(baseAddresses) > 1: 68 raise RuntimeError( 69 'Multiple base address found, please disable ASLR and collect ' 70 'profile again') 71 if not len(baseAddresses): 72 raise RuntimeError('Could not find the base address in the profile') 73 self.loading_address = baseAddresses.pop() 74 75 def RemoveFiles(self): 76 shutil.rmtree(self.tempDir) 77 if os.path.isfile(os.path.join(os.getcwd(), 'out.txt')): 78 os.remove(os.path.join(os.getcwd(), 'out.txt')) 79 if os.path.isfile(os.path.join(os.getcwd(), 'inst-histo.txt')): 80 os.remove(os.path.join(os.getcwd(), 'inst-histo.txt')) 81 82 def getHeatmap(self): 83 if not self.loading_address: 84 return 85 heatmap_script = os.path.join(self.dir, 'perf-to-inst-page.sh') 86 cmd = '{0} {1} {2} {3} {4}'.format(heatmap_script, self.binary, 87 self.perf_report, self.loading_address, 88 self.page_size) 89 retval = self.ce.RunCommand(cmd) 90 if retval: 91 raise RuntimeError('Failed to run script to generate heatmap') 92 93 94def main(argv): 95 """Parse the options. 96 97 Args: 98 argv: The options with which this script was invoked. 99 100 Returns: 101 0 unless an exception is raised. 102 """ 103 parser = argparse.ArgumentParser() 104 105 parser.add_argument( 106 '--chromeos_root', 107 dest='chromeos_root', 108 required=True, 109 help='ChromeOS root to use for generate heatmaps.') 110 parser.add_argument( 111 '--perf_data', dest='perf_data', required=True, help='The raw perf data.') 112 parser.add_argument( 113 '--binary', 114 dest='binary', 115 required=False, 116 help='The name of the binary.', 117 default='chrome') 118 parser.add_argument( 119 '--page_size', 120 dest='page_size', 121 required=False, 122 help='The page size for heat maps.', 123 default=4096) 124 options = parser.parse_args(argv) 125 126 if not IsARepoRoot(options.chromeos_root): 127 parser.error('% does not contain .repo dir.' % options.chromeos_root) 128 129 if not os.path.isfile(options.perf_data): 130 parser.error('Cannot find perf_data: %s.' % options.perf_data) 131 132 heatmap_producer = HeatMapProducer(options.chromeos_root, options.perf_data, 133 options.page_size, options.binary) 134 try: 135 heatmap_producer.copyFileToChroot() 136 heatmap_producer.getPerfReport() 137 heatmap_producer.getBinaryBaseAddress() 138 heatmap_producer.getHeatmap() 139 print('\nheat map and time histgram genereated in the current directory ' 140 'with name heat_map.png and timeline.png accordingly.') 141 except RuntimeError, e: 142 print(e) 143 finally: 144 heatmap_producer.RemoveFiles() 145 146 147if __name__ == '__main__': 148 sys.exit(main(sys.argv[1:])) 149