| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # Copyright 2019 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Spawns off an AFDO tryjob. |
| |
| This tryjob will cause perf profiles to be collected as though we were running |
| our benchmark AFDO pipeline. Depending on the set of flags that you use, |
| different things will happen. Any artifacts will land in |
| gs://chromeos-localmirror/distfiles/afdo/experimental/approximation |
| |
| This tryjob will generate *either* a full (AFDO profile, perf.data, |
| chrome.debug) combo, or just a perf.data, depending on the arguments you feed |
| it. |
| |
| The thing to be careful of is that our localmirror bucket is shared between |
| everyone, so it's super easy for two AFDO profile runs to 'collide'. Hence, if |
| you provide the --tag_profiles_with_current_time flag, the script will generate |
| *only* a perf.data, but that perf.data will have a timestamp (with second |
| resolution) on it. This makes collisions super unlikely. |
| |
| If you'd like to know which perf profile was yours: |
| - Go to the tryjob output page |
| - Look for 'HWTest [AFDO_Record]' |
| - Click on its stdout |
| - Find "Links to test logs:" in the stdout |
| - Follow the link by telemetry_AFDOGenerate |
| - Find and click the link to debug/autoserv.DEBUG |
| - Look for a gs:// link ending in `.perf.data` with a compression suffix |
| (currently `.bz2`; maybe `.xz` eventually). That's the gs:// path to your |
| perf profile. |
| |
| The downside to this option is that there's no (reliable + trivial to |
| implement) way for the bot that converts AFDO profiles into perf profiles to |
| know the profile to choose. So, you're stuck generating a profile on your own. |
| We have a tool for just that. Please see `generate_afdo_from_tryjob.py`. |
| |
| If you don't like that tool, generating your own profile isn't super difficult. |
| Just grab the perf profile that your logs note from gs://, grab a copy of |
| chrome.debug from your tryjob, and use `create_llvm_prof` to create a profile. |
| |
| On the other hand, if you're 100% sure that your profile won't collide, you can |
| make your life easier by providing --use_afdo_generation_stage. |
| |
| If you provide neither --use_afdo_generation_stage nor |
| --tag_profiles_with_current_time, --tag_profiles_with_current_time is implied, |
| since it's safer. |
| """ |
| |
| |
| import argparse |
| import collections |
| import pipes |
| import subprocess |
| import sys |
| import time |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser( |
| description=__doc__, |
| formatter_class=argparse.RawDescriptionHelpFormatter, |
| ) |
| parser.add_argument( |
| "--force_no_patches", |
| action="store_true", |
| help="Run even if no patches are provided", |
| ) |
| parser.add_argument( |
| "--tag_profiles_with_current_time", |
| action="store_true", |
| help="Perf profile names will have the current time added to them.", |
| ) |
| parser.add_argument( |
| "--use_afdo_generation_stage", |
| action="store_true", |
| help="Perf profiles will be automatically converted to AFDO profiles.", |
| ) |
| parser.add_argument( |
| "-g", |
| "--patch", |
| action="append", |
| default=[], |
| help="A patch to add to the AFDO run", |
| ) |
| parser.add_argument( |
| "-n", |
| "--dry_run", |
| action="store_true", |
| help="Just print the command that would be run", |
| ) |
| args = parser.parse_args() |
| |
| dry_run = args.dry_run |
| force_no_patches = args.force_no_patches |
| tag_profiles_with_current_time = args.tag_profiles_with_current_time |
| use_afdo_generation_stage = args.use_afdo_generation_stage |
| user_patches = args.patch |
| |
| if tag_profiles_with_current_time and use_afdo_generation_stage: |
| raise ValueError( |
| "You can't tag profiles with the time + have " "afdo-generate" |
| ) |
| |
| if not tag_profiles_with_current_time and not use_afdo_generation_stage: |
| print( |
| "Neither current_time nor afdo_generate asked for. Assuming you " |
| "prefer current time tagging." |
| ) |
| print("You have 5 seconds to cancel and try again.") |
| print() |
| if not dry_run: |
| time.sleep(5) |
| tag_profiles_with_current_time = True |
| |
| patches = [ |
| # Send profiles to localmirror instead of chromeos-prebuilt. This should |
| # always be done, since sending profiles into production is bad. :) |
| # https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/1436158 |
| 1436158, |
| # Force profile generation. Otherwise, we'll decide to not spawn off the |
| # perf hwtests. |
| # https://chromium-review.googlesource.com/c/chromiumos/chromite/+/1313291 |
| 1313291, |
| ] |
| |
| if tag_profiles_with_current_time: |
| # Tags the profiles with the current time of day. As detailed in the |
| # docstring, this is desirable unless you're sure that this is the only |
| # experimental profile that will be generated today. |
| # https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/1436157 |
| patches.append(1436157) |
| |
| if use_afdo_generation_stage: |
| # Make the profile generation stage look in localmirror, instead of having |
| # it look in chromeos-prebuilt. Without this, we'll never upload |
| # chrome.debug or try to generate an AFDO profile. |
| # https://chromium-review.googlesource.com/c/chromiumos/chromite/+/1436583 |
| patches.append(1436583) |
| |
| if not user_patches and not force_no_patches: |
| raise ValueError( |
| "No patches given; pass --force_no_patches to force a " "tryjob" |
| ) |
| |
| for patch in user_patches: |
| # We accept two formats. Either a URL that ends with a number, or a number. |
| if patch.startswith("http"): |
| patch = patch.split("/")[-1] |
| patches.append(int(patch)) |
| |
| count = collections.Counter(patches) |
| too_many = [k for k, v in count.items() if v > 1] |
| if too_many: |
| too_many.sort() |
| raise ValueError( |
| "Patch(es) asked for application more than once: %s" % too_many |
| ) |
| |
| args = [ |
| "cros", |
| "tryjob", |
| ] |
| |
| for patch in patches: |
| args += ["-g", str(patch)] |
| |
| args += [ |
| "--nochromesdk", |
| "--hwtest", |
| "chell-chrome-pfq-tryjob", |
| ] |
| |
| print(" ".join(pipes.quote(a) for a in args)) |
| if not dry_run: |
| sys.exit(subprocess.call(args)) |
| |
| |
| if __name__ == "__main__": |
| main() |