• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2019 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Spawns off an AFDO tryjob.
8
9This tryjob will cause perf profiles to be collected as though we were running
10our benchmark AFDO pipeline. Depending on the set of flags that you use,
11different things will happen. Any artifacts will land in
12gs://chromeos-localmirror/distfiles/afdo/experimental/approximation
13
14This tryjob will generate *either* a full (AFDO profile, perf.data,
15chrome.debug) combo, or just a perf.data, depending on the arguments you feed
16it.
17
18The thing to be careful of is that our localmirror bucket is shared between
19everyone, so it's super easy for two AFDO profile runs to 'collide'. Hence, if
20you provide the --tag_profiles_with_current_time flag, the script will generate
21*only* a perf.data, but that perf.data will have a timestamp (with second
22resolution) on it. This makes collisions super unlikely.
23
24If you'd like to know which perf profile was yours:
25  - Go to the tryjob output page
26  - Look for 'HWTest [AFDO_Record]'
27  - Click on its stdout
28  - Find "Links to test logs:" in the stdout
29  - Follow the link by telemetry_AFDOGenerate
30  - Find and click the link to debug/autoserv.DEBUG
31  - Look for a gs:// link ending in `.perf.data` with a compression suffix
32    (currently `.bz2`; maybe `.xz` eventually). That's the gs:// path to your
33    perf profile.
34
35The downside to this option is that there's no (reliable + trivial to
36implement) way for the bot that converts AFDO profiles into perf profiles to
37know the profile to choose. So, you're stuck generating a profile on your own.
38We have a tool for just that. Please see `generate_afdo_from_tryjob.py`.
39
40If you don't like that tool, generating your own profile isn't super difficult.
41Just grab the perf profile that your logs note from gs://, grab a copy of
42chrome.debug from your tryjob, and use `create_llvm_prof` to create a profile.
43
44On the other hand, if you're 100% sure that your profile won't collide, you can
45make your life easier by providing --use_afdo_generation_stage.
46
47If you provide neither --use_afdo_generation_stage nor
48--tag_profiles_with_current_time, --tag_profiles_with_current_time is implied,
49since it's safer.
50"""
51
52from __future__ import print_function
53
54import argparse
55import collections
56import pipes
57import subprocess
58import sys
59import time
60
61
62def main():
63  parser = argparse.ArgumentParser(
64      description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
65  parser.add_argument(
66      '--force_no_patches',
67      action='store_true',
68      help='Run even if no patches are provided')
69  parser.add_argument(
70      '--tag_profiles_with_current_time',
71      action='store_true',
72      help='Perf profile names will have the current time added to them.')
73  parser.add_argument(
74      '--use_afdo_generation_stage',
75      action='store_true',
76      help='Perf profiles will be automatically converted to AFDO profiles.')
77  parser.add_argument(
78      '-g',
79      '--patch',
80      action='append',
81      default=[],
82      help='A patch to add to the AFDO run')
83  parser.add_argument(
84      '-n',
85      '--dry_run',
86      action='store_true',
87      help='Just print the command that would be run')
88  args = parser.parse_args()
89
90  dry_run = args.dry_run
91  force_no_patches = args.force_no_patches
92  tag_profiles_with_current_time = args.tag_profiles_with_current_time
93  use_afdo_generation_stage = args.use_afdo_generation_stage
94  user_patches = args.patch
95
96  if tag_profiles_with_current_time and use_afdo_generation_stage:
97    raise ValueError("You can't tag profiles with the time + have "
98                     'afdo-generate')
99
100  if not tag_profiles_with_current_time and not use_afdo_generation_stage:
101    print('Neither current_time nor afdo_generate asked for. Assuming you '
102          'prefer current time tagging.')
103    print('You have 5 seconds to cancel and try again.')
104    print()
105    if not dry_run:
106      time.sleep(5)
107    tag_profiles_with_current_time = True
108
109  patches = [
110      # Send profiles to localmirror instead of chromeos-prebuilt. This should
111      # always be done, since sending profiles into production is bad. :)
112      # https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/1436158
113      1436158,
114      # Force profile generation. Otherwise, we'll decide to not spawn off the
115      # perf hwtests.
116      # https://chromium-review.googlesource.com/c/chromiumos/chromite/+/1313291
117      1313291,
118  ]
119
120  if tag_profiles_with_current_time:
121    # Tags the profiles with the current time of day. As detailed in the
122    # docstring, this is desirable unless you're sure that this is the only
123    # experimental profile that will be generated today.
124    # https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/1436157
125    patches.append(1436157)
126
127  if use_afdo_generation_stage:
128    # Make the profile generation stage look in localmirror, instead of having
129    # it look in chromeos-prebuilt. Without this, we'll never upload
130    # chrome.debug or try to generate an AFDO profile.
131    # https://chromium-review.googlesource.com/c/chromiumos/chromite/+/1436583
132    patches.append(1436583)
133
134  if not user_patches and not force_no_patches:
135    raise ValueError('No patches given; pass --force_no_patches to force a '
136                     'tryjob')
137
138  for patch in user_patches:
139    # We accept two formats. Either a URL that ends with a number, or a number.
140    if patch.startswith('http'):
141      patch = patch.split('/')[-1]
142    patches.append(int(patch))
143
144  count = collections.Counter(patches)
145  too_many = [k for k, v in count.items() if v > 1]
146  if too_many:
147    too_many.sort()
148    raise ValueError(
149        'Patch(es) asked for application more than once: %s' % too_many)
150
151  args = [
152      'cros',
153      'tryjob',
154  ]
155
156  for patch in patches:
157    args += ['-g', str(patch)]
158
159  args += [
160      '--nochromesdk',
161      '--hwtest',
162      'chell-chrome-pfq-tryjob',
163  ]
164
165  print(' '.join(pipes.quote(a) for a in args))
166  if not dry_run:
167    sys.exit(subprocess.call(args))
168
169
170if __name__ == '__main__':
171  main()
172