You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
6.5 KiB
187 lines
6.5 KiB
# Copyright 2011 Google Inc. All Rights Reserved. |
|
"""Summarize hottest basic blocks found while doing a ChromeOS FDO build. |
|
|
|
Here is an example execution: |
|
|
|
summarize_hot_blocks.py |
|
--data_dir=~/chromeos/chroot/var/cache/chromeos-chrome/ --cutoff=10000 |
|
--output_dir=/home/x/y |
|
|
|
With the cutoff, it will ignore any basic blocks that have a count less |
|
than what is specified (in this example 10000) |
|
The script looks inside the directory (this is typically a directory where |
|
the object files are generated) for files with *.profile and *.optimized |
|
suffixes. To get these, the following flags were added to the compiler |
|
invokation within vanilla_vs_fdo.py in the profile-use phase. |
|
|
|
"-fdump-tree-optimized-blocks-lineno " |
|
"-fdump-ipa-profile-blocks-lineno " |
|
|
|
Here is an example of the *.profile and *.optimized files contents: |
|
|
|
# BLOCK 7 freq:3901 count:60342, starting at line 92 |
|
# PRED: 6 [39.0%] count:60342 (true,exec) |
|
[url_canon_internal.cc : 92:28] MEM[(const char * *)source_6(D) + 16B] = |
|
D.28080_17; |
|
[url_canon_internal.cc : 93:41] MEM[(struct Component *)parsed_4(D) + 16B] = |
|
MEM[(const struct Component &)repl_1(D) + 80]; |
|
# SUCC: 8 [100.0%] count:60342 (fallthru,exec) |
|
# BLOCK 8 freq:10000 count:154667, starting at line 321 |
|
# PRED: 7 [100.0%] count:60342 (fallthru,exec) 6 [61.0%] count:94325 |
|
(false,exec) |
|
[url_canon_internal.cc : 321:51] # DEBUG D#10 => |
|
[googleurl/src/url_canon_internal.cc : 321] &parsed_4(D)->host |
|
|
|
this script finds the blocks with highest count and shows the first line |
|
of each block so that it is easy to identify the origin of the basic block. |
|
|
|
""" |
|
|
|
__author__ = 'llozano@google.com (Luis Lozano)' |
|
|
|
import optparse |
|
import os |
|
import re |
|
import shutil |
|
import sys |
|
import tempfile |
|
|
|
from cros_utils import command_executer |
|
|
|
|
|
# Given a line, check if it has a block count and return it. |
|
# Return -1 if there is no match |
|
def GetBlockCount(line): |
|
match_obj = re.match('.*# BLOCK \d+ .*count:(\d+)', line) |
|
if match_obj: |
|
return int(match_obj.group(1)) |
|
else: |
|
return -1 |
|
|
|
|
|
class Collector(object): |
|
|
|
def __init__(self, data_dir, cutoff, output_dir, tempdir): |
|
self._data_dir = data_dir |
|
self._cutoff = cutoff |
|
self._output_dir = output_dir |
|
self._tempdir = tempdir |
|
self._ce = command_executer.GetCommandExecuter() |
|
|
|
def CollectFileList(self, file_exp, list_file): |
|
command = ("find %s -type f -name '%s' > %s" % |
|
(self._data_dir, file_exp, |
|
os.path.join(self._tempdir, list_file))) |
|
ret = self._ce.RunCommand(command) |
|
if ret: |
|
raise RuntimeError('Failed: %s' % command) |
|
|
|
def SummarizeLines(self, data_file): |
|
sum_lines = [] |
|
search_lno = False |
|
for line in data_file: |
|
count = GetBlockCount(line) |
|
if count != -1: |
|
if count >= self._cutoff: |
|
search_lno = True |
|
sum_line = line.strip() |
|
sum_count = count |
|
# look for a line that starts with line number information |
|
elif search_lno and re.match('^\s*\[.*: \d*:\d*]', line): |
|
search_lno = False |
|
sum_lines.append('%d:%s: %s %s' % |
|
(sum_count, data_file.name, sum_line, line)) |
|
return sum_lines |
|
|
|
# Look for blocks in the data file that have a count larger than the cutoff |
|
# and generate a sorted summary file of the hottest blocks. |
|
def SummarizeFile(self, data_file, sum_file): |
|
with open(data_file, 'r') as f: |
|
sum_lines = self.SummarizeLines(f) |
|
|
|
# sort reverse the list in place by the block count number |
|
sum_lines.sort(key=GetBlockCount, reverse=True) |
|
|
|
with open(sum_file, 'w') as sf: |
|
sf.write(''.join(sum_lines)) |
|
|
|
print 'Generated file Summary: ', sum_file |
|
|
|
# Find hottest blocks in the list of files, generate a sorted summary for |
|
# each file and then do a sorted merge of all the summaries. |
|
def SummarizeList(self, list_file, summary_file): |
|
with open(os.path.join(self._tempdir, list_file)) as f: |
|
sort_list = [] |
|
for file_name in f: |
|
file_name = file_name.strip() |
|
sum_file = '%s.sum' % file_name |
|
sort_list.append('%s%s' % (sum_file, chr(0))) |
|
self.SummarizeFile(file_name, sum_file) |
|
|
|
tmp_list_file = os.path.join(self._tempdir, 'file_list.dat') |
|
with open(tmp_list_file, 'w') as file_list_file: |
|
for x in sort_list: |
|
file_list_file.write(x) |
|
|
|
merge_command = ('sort -nr -t: -k1 --merge --files0-from=%s > %s ' % |
|
(tmp_list_file, summary_file)) |
|
|
|
ret = self._ce.RunCommand(merge_command) |
|
if ret: |
|
raise RuntimeError('Failed: %s' % merge_command) |
|
print 'Generated general summary: ', summary_file |
|
|
|
def SummarizePreOptimized(self, summary_file): |
|
self.CollectFileList('*.profile', 'chrome.profile.list') |
|
self.SummarizeList('chrome.profile.list', |
|
os.path.join(self._output_dir, summary_file)) |
|
|
|
def SummarizeOptimized(self, summary_file): |
|
self.CollectFileList('*.optimized', 'chrome.optimized.list') |
|
self.SummarizeList('chrome.optimized.list', |
|
os.path.join(self._output_dir, summary_file)) |
|
|
|
|
|
def Main(argv): |
|
command_executer.InitCommandExecuter() |
|
usage = ('usage: %prog --data_dir=<dir> --cutoff=<value> ' |
|
'--output_dir=<dir> [--keep_tmp]') |
|
parser = optparse.OptionParser(usage=usage) |
|
parser.add_option('--data_dir', |
|
dest='data_dir', |
|
help=('directory where the FDO (*.profile and ' |
|
'*.optimized) files are located')) |
|
parser.add_option('--cutoff', |
|
dest='cutoff', |
|
help='Minimum count to consider for each basic block') |
|
parser.add_option('--output_dir', |
|
dest='output_dir', |
|
help=('directory where summary data will be generated' |
|
'(pre_optimized.txt, optimized.txt)')) |
|
parser.add_option('--keep_tmp', |
|
action='store_true', |
|
dest='keep_tmp', |
|
default=False, |
|
help=('Keep directory with temporary files' |
|
'(for debugging purposes)')) |
|
options = parser.parse_args(argv)[0] |
|
if not all((options.data_dir, options.cutoff, options.output_dir)): |
|
parser.print_help() |
|
sys.exit(1) |
|
|
|
tempdir = tempfile.mkdtemp() |
|
|
|
co = Collector(options.data_dir, int(options.cutoff), options.output_dir, |
|
tempdir) |
|
co.SummarizePreOptimized('pre_optimized.txt') |
|
co.SummarizeOptimized('optimized.txt') |
|
|
|
if not options.keep_tmp: |
|
shutil.rmtree(tempdir, ignore_errors=True) |
|
|
|
return 0 |
|
|
|
|
|
if __name__ == '__main__': |
|
retval = Main(sys.argv) |
|
sys.exit(retval)
|
|
|