OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright 2017 The Chromium 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 |
| 6 """Merge the PGC files generated during the profiling step to the PGD database. |
| 7 |
| 8 This is required to workaround a flakyness in pgomgr.exe where it can run out |
| 9 of address space while trying to merge all the PGC files at the same time. |
| 10 """ |
| 11 |
| 12 import glob |
| 13 import json |
| 14 import optparse |
| 15 import os |
| 16 import subprocess |
| 17 import sys |
| 18 |
| 19 |
| 20 def find_pgomgr(chrome_checkout_dir): |
| 21 """Find pgomgr.exe.""" |
| 22 win_toolchain_json_file = os.path.join(chrome_checkout_dir, 'build', |
| 23 'win_toolchain.json') |
| 24 if not os.path.exists(win_toolchain_json_file): |
| 25 raise Exception('The toolchain JSON file is missing.') |
| 26 with open(win_toolchain_json_file) as temp_f: |
| 27 toolchain_data = json.load(temp_f) |
| 28 if not os.path.isdir(toolchain_data['path']): |
| 29 raise Exception('The toolchain JSON file is invalid.') |
| 30 |
| 31 # Always use the x64 version of pgomgr (the x86 one doesn't work on the bot's |
| 32 # environment). |
| 33 pgomgr_dir = os.path.join(toolchain_data['path'], 'VC', 'bin', 'amd64') |
| 34 |
| 35 pgomgr_path = os.path.join(pgomgr_dir, 'pgomgr.exe') |
| 36 if not os.path.exists(pgomgr_path): |
| 37 raise Exception('pgomgr.exe is missing from %s.' % pgomgr_dir) |
| 38 |
| 39 return pgomgr_path |
| 40 |
| 41 |
| 42 def main(): |
| 43 parser = optparse.OptionParser(usage='%prog [options]') |
| 44 parser.add_option('--checkout-dir', help='The Chrome checkout directory.') |
| 45 parser.add_option('--target-cpu', help='[DEPRECATED] The target\'s bitness.') |
| 46 parser.add_option('--build-dir', help='Chrome build directory.') |
| 47 parser.add_option('--binary-name', help='The binary for which the PGC files ' |
| 48 'should be merged, without extension.') |
| 49 options, _ = parser.parse_args() |
| 50 |
| 51 if not options.checkout_dir: |
| 52 parser.error('--checkout-dir is required') |
| 53 if not options.build_dir: |
| 54 parser.error('--build-dir is required') |
| 55 if not options.binary_name: |
| 56 parser.error('--binary-name is required') |
| 57 |
| 58 # Starts by finding pgomgr.exe. |
| 59 pgomgr_path = find_pgomgr(options.checkout_dir) |
| 60 |
| 61 pgc_files = glob.glob(os.path.join(options.build_dir, |
| 62 '%s*.pgc' % options.binary_name)) |
| 63 |
| 64 # Number of PGC files that should be merged in each iterations, merging all |
| 65 # the files one by one is really slow but merging more to 10 at a time doesn't |
| 66 # really seem to impact the total time. |
| 67 # |
| 68 # Number of pgc merged per iteration | Time (in min) |
| 69 # 1 | 27.2 |
| 70 # 10 | 12.8 |
| 71 # 20 | 12.0 |
| 72 # 30 | 11.5 |
| 73 # 40 | 11.4 |
| 74 # 50 | 11.5 |
| 75 # 60 | 11.6 |
| 76 # 70 | 11.6 |
| 77 # 80 | 11.7 |
| 78 # |
| 79 # TODO(sebmarchand): Measure the memory usage of pgomgr.exe to see how it get |
| 80 # affected by the number of pgc files. |
| 81 pgc_per_iter = 20 |
| 82 |
| 83 def _split_in_chunks(items, chunk_size): |
| 84 """Split |items| in chunks of size |chunk_size|. |
| 85 |
| 86 Source: http://stackoverflow.com/a/312464 |
| 87 """ |
| 88 for i in xrange(0, len(items), chunk_size): |
| 89 yield items[i:i + chunk_size] |
| 90 |
| 91 for chunk in _split_in_chunks(pgc_files, pgc_per_iter): |
| 92 merge_command = [ |
| 93 pgomgr_path, |
| 94 '/merge' |
| 95 ] |
| 96 for pgc_file in chunk: |
| 97 merge_command.append([ |
| 98 os.path.join(options.build_dir, os.path.basename(pgc_file)) |
| 99 ]) |
| 100 |
| 101 merge_command.append([ |
| 102 os.path.join(options.build_dir, '%s.pgd' % options.binary_name) |
| 103 ]) |
| 104 proc = subprocess.Popen(merge_command, stdout=subprocess.PIPE) |
| 105 stdout, stderr = proc.communicate() |
| 106 print stdout |
| 107 if proc.returncode != 0: |
| 108 raise Exception('Error while trying to merge the PGC files:\n%s' % stderr) |
| 109 |
| 110 |
| 111 if __name__ == '__main__': |
| 112 sys.exit(main()) |
OLD | NEW |