Zhizhou Yang | 726ded5 | 2017-08-23 22:47:50 -0700 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | # Script to generate and collect PGO data based on benchmark |
| 4 | from __future__ import print_function |
| 5 | |
| 6 | import argparse |
| 7 | import config |
| 8 | import logging |
| 9 | import os |
| 10 | import subprocess |
| 11 | import sys |
| 12 | import tempfile |
| 13 | |
| 14 | # Turn the logging level to INFO before importing other code, to avoid having |
| 15 | # failed import logging messages confuse the user. |
| 16 | logging.basicConfig(level=logging.INFO) |
| 17 | |
| 18 | def _parse_arguments_internal(argv): |
| 19 | """ |
| 20 | Parse command line arguments |
| 21 | |
| 22 | @param argv: argument list to parse |
| 23 | |
| 24 | @returns: tuple of parsed arguments and argv suitable for remote runs |
| 25 | |
| 26 | @raises SystemExit if arguments are malformed, or required arguments |
| 27 | are not present. |
| 28 | """ |
| 29 | |
| 30 | parser = argparse.ArgumentParser(description='Run this script to collect ' |
| 31 | 'PGO data.') |
| 32 | |
| 33 | parser.add_argument('-b', '--bench', |
| 34 | help='Select which benchmark to collect profdata.') |
| 35 | |
| 36 | parser.add_argument('-d', '--pathDUT', default='/data/local/tmp', |
| 37 | help='Specify where to generate PGO data on device, ' |
| 38 | 'set to /data/local/tmp by default.') |
| 39 | |
| 40 | parser.add_argument('-p', '--path', default=config.bench_suite_dir, |
| 41 | help='Specify the location to put the profdata, set ' |
| 42 | ' to bench_suite_dir by default.') |
| 43 | |
| 44 | parser.add_argument('-s', '--serial', |
| 45 | help='Device serial number.') |
| 46 | |
| 47 | parser.add_argument('-r', '--remote', default='localhost', |
| 48 | help='hostname[:port] if the ADB device is connected ' |
| 49 | 'to a remote machine. Ensure this workstation ' |
| 50 | 'is configured for passwordless ssh access as ' |
| 51 | 'users "root" or "adb"') |
| 52 | return parser.parse_args(argv) |
| 53 | |
| 54 | # Call run.py to build benchmark with -fprofile-generate flags and run on DUT |
| 55 | def run_suite(bench, serial, remote, pathDUT): |
| 56 | logging.info('Build and run instrumented benchmark...') |
| 57 | run_cmd = ['./run.py', '-b=' + bench] |
| 58 | if serial: |
| 59 | run_cmd.append('-s=' + serial) |
| 60 | run_cmd.append('-r=' + remote) |
| 61 | run_cmd.append('-f=-fprofile-generate=%s' % pathDUT) |
| 62 | run_cmd.append('--ldflags=-fprofile-generate=%s' % pathDUT) |
| 63 | try: |
| 64 | subprocess.check_call(run_cmd) |
| 65 | except subprocess.CalledProcessError: |
| 66 | logging.error('Error running %s.', run_cmd) |
| 67 | raise |
| 68 | |
| 69 | # Pull profraw data from device using pull_device.py script in autotest utils. |
| 70 | def pull_result(bench, serial, remote, pathDUT, path): |
| 71 | logging.info('Pulling profraw data from device to local') |
| 72 | pull_cmd = [os.path.join(config.android_home, |
| 73 | config.autotest_dir, |
| 74 | 'site_utils/pull_device.py')] |
| 75 | pull_cmd.append('-b=' + bench) |
| 76 | pull_cmd.append('-r=' + remote) |
| 77 | if serial: |
| 78 | pull_cmd.append('-s=' + serial) |
| 79 | pull_cmd.append('-p=' + path) |
| 80 | pull_cmd.append('-d=' + pathDUT) |
| 81 | try: |
| 82 | subprocess.check_call(pull_cmd) |
| 83 | except: |
| 84 | logging.error('Error while pulling profraw data.') |
| 85 | raise |
| 86 | |
| 87 | # Use llvm-profdata tool to convert profraw data to the format llvm can |
| 88 | # recgonize. |
| 89 | def merge(bench, pathDUT, path): |
| 90 | logging.info('Generate profdata for PGO...') |
| 91 | # Untar the compressed rawdata file collected from device |
| 92 | tmp_dir = tempfile.mkdtemp() |
| 93 | untar_cmd = ['tar', |
| 94 | '-xf', |
| 95 | os.path.join(path, bench + '_profraw.tar'), |
| 96 | '-C', |
| 97 | tmp_dir] |
| 98 | |
| 99 | # call llvm-profdata to merge the profraw data |
| 100 | profdata = os.path.join(path, bench + '.profdata') |
| 101 | merge_cmd = ['llvm-profdata', |
| 102 | 'merge', |
| 103 | '-output=' + profdata, |
| 104 | tmp_dir + pathDUT] |
| 105 | try: |
| 106 | subprocess.check_call(untar_cmd) |
| 107 | subprocess.check_call(merge_cmd) |
| 108 | logging.info('Profdata is generated successfully, located at %s', |
| 109 | profdata) |
| 110 | except: |
| 111 | logging.error('Error while merging profraw data.') |
| 112 | raise |
| 113 | finally: |
| 114 | subprocess.check_call(['rm', '-rf', tmp_dir]) |
| 115 | |
| 116 | def main(argv): |
| 117 | """ |
| 118 | Entry point for nightly_run script. |
| 119 | |
| 120 | @param argv: arguments list |
| 121 | """ |
| 122 | arguments = _parse_arguments_internal(argv) |
| 123 | |
| 124 | bench = arguments.bench |
| 125 | serial = arguments.serial |
| 126 | path = arguments.path |
| 127 | remote = arguments.remote |
| 128 | |
| 129 | # Create a profraw directory to collect data |
| 130 | pathDUT = os.path.join(arguments.pathDUT, bench + '_profraw') |
| 131 | |
| 132 | run_suite(bench, serial, remote, pathDUT) |
| 133 | |
| 134 | pull_result(bench, serial, remote, pathDUT, path) |
| 135 | |
| 136 | merge(bench, pathDUT, path) |
| 137 | |
| 138 | if __name__ == '__main__': |
| 139 | main(sys.argv[1:]) |