[PDNCF] Python 3.12 compatibility
[platform/framework/web/chromium-efl.git] / tools / update_pgo_profiles.py
1 #!/usr/bin/env python
2 # Copyright 2020 The Chromium Authors
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5 """Downloads pgo profiles for optimizing official Chrome.
6
7 This script has the following responsibilities:
8 1. Download a requested profile if necessary.
9 2. Return a path to the current profile to feed to the build system.
10 3. Removed stale profiles (2 days) to save disk spaces because profiles are
11    large (~1GB) and updated frequently (~4 times a day).
12 """
13
14 from __future__ import print_function
15
16 import argparse
17 import os
18 import sys
19 import time
20
21 _SRC_ROOT = os.path.abspath(
22     os.path.join(os.path.dirname(__file__), os.path.pardir))
23 sys.path.append(os.path.join(_SRC_ROOT, 'third_party', 'depot_tools'))
24 import download_from_google_storage
25
26 sys.path.append(os.path.join(_SRC_ROOT, 'build'))
27 import gn_helpers
28
29 # Absolute path to the directory that stores pgo related state files, which
30 # specifcies which profile to update and use.
31 _PGO_DIR = os.path.join(_SRC_ROOT, 'chrome', 'build')
32
33 # Absolute path to the directory that stores pgo profiles.
34 _PGO_PROFILE_DIR = os.path.join(_PGO_DIR, 'pgo_profiles')
35
36
37 def _read_profile_name(target):
38   """Read profile name given a target.
39
40   Args:
41     target(str): The target name, such as win32, mac.
42
43   Returns:
44     Name of the profile to update and use, such as:
45     chrome-win32-master-67ad3c89d2017131cc9ce664a1580315517550d1.profdata.
46   """
47   state_file = os.path.join(_PGO_DIR, '%s.pgo.txt' % target)
48   with open(state_file, 'r') as f:
49     profile_name = f.read().strip()
50
51   return profile_name
52
53
54 def _remove_unused_profiles(current_profile_name):
55   """Removes unused profiles, except the current one, to save disk space."""
56   days = 2
57   expiration_duration = 60 * 60 * 24 * days
58   for f in os.listdir(_PGO_PROFILE_DIR):
59     if f == current_profile_name:
60       continue
61
62     p = os.path.join(_PGO_PROFILE_DIR, f)
63     age = time.time() - os.path.getmtime(p)
64     if age > expiration_duration:
65       print('Removing profile %s as it hasn\'t been used in the past %d days' %
66             (p, days))
67       os.remove(p)
68
69
70 def _update(args):
71   """Update profile if necessary according to the state file.
72
73   Args:
74     args(dict): A dict of cmd arguments, such as target and gs_url_base.
75
76   Raises:
77     RuntimeError: If failed to download profiles from gcs.
78   """
79   profile_name = _read_profile_name(args.target)
80   profile_path = os.path.join(_PGO_PROFILE_DIR, profile_name)
81   if os.path.isfile(profile_path):
82     os.utime(profile_path, None)
83     return
84
85   gsutil = download_from_google_storage.Gsutil(
86       download_from_google_storage.GSUTIL_DEFAULT_PATH)
87   gs_path = 'gs://' + args.gs_url_base.strip('/') + '/' + profile_name
88   code = gsutil.call('cp', gs_path, profile_path)
89   if code != 0:
90     raise RuntimeError('gsutil failed to download "%s"' % gs_path)
91
92   _remove_unused_profiles(profile_name)
93
94
95 def _get_profile_path(args):
96   """Returns an absolute path to the current profile.
97
98   Args:
99     args(dict): A dict of cmd arguments, such as target and gs_url_base.
100
101   Raises:
102     RuntimeError: If the current profile is missing.
103   """
104   profile_path = os.path.join(_PGO_PROFILE_DIR, _read_profile_name(args.target))
105   if not os.path.isfile(profile_path):
106     raise RuntimeError(
107         'requested profile "%s" doesn\'t exist, please make sure '
108         '"checkout_pgo_profiles" is set to True in the "custom_vars" section '
109         'of your .gclient file, e.g.: \n'
110         'solutions = [ \n'
111         '  { \n'
112         '    "name": "src", \n'
113         '    # ...  \n'
114         '    "custom_vars": { \n'
115         '      "checkout_pgo_profiles": True, \n'
116         '    }, \n'
117         '  }, \n'
118         '], \n'
119         'and then run "gclient runhooks" to download it. You can also simply '
120         'disable the PGO optimizations by setting |chrome_pgo_phase = 0| in '
121         'your GN arguments.'%
122         profile_path)
123
124   os.utime(profile_path, None)
125   profile_path.rstrip(os.sep)
126   print(gn_helpers.ToGNString(profile_path))
127
128
129 def main():
130   parser = argparse.ArgumentParser(
131       description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
132   parser.add_argument(
133       '--target',
134       required=True,
135       choices=[
136           'win-arm64',
137           'win32',
138           'win64',
139           'mac',
140           'mac-arm',
141           'linux',
142           'lacros64',
143           'lacros-arm',
144           'lacros-arm64',
145           'android-arm32',
146           'android-arm64',
147       ],
148       help='Identifier of a specific target platform + architecture.')
149   subparsers = parser.add_subparsers()
150
151   parser_update = subparsers.add_parser('update')
152   parser_update.add_argument(
153       '--gs-url-base',
154       required=True,
155       help='The base GS URL to search for the profile.')
156   parser_update.set_defaults(func=_update)
157
158   parser_get_profile_path = subparsers.add_parser('get_profile_path')
159   parser_get_profile_path.set_defaults(func=_get_profile_path)
160
161   args = parser.parse_args()
162   return args.func(args)
163
164
165 if __name__ == '__main__':
166   sys.exit(main())