191 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
| #!/usr/bin/env python3
 | |
| # Copyright (C) 2021 The Android Open Source Project
 | |
| #
 | |
| # Licensed under the Apache License, Version 2.0 (the "License");
 | |
| # you may not use this file except in compliance with the License.
 | |
| # You may obtain a copy of the License at
 | |
| #
 | |
| #      http://www.apache.org/licenses/LICENSE-2.0
 | |
| #
 | |
| # Unless required by applicable law or agreed to in writing, software
 | |
| # distributed under the License is distributed on an "AS IS" BASIS,
 | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| # See the License for the specific language governing permissions and
 | |
| # limitations under the License.
 | |
| """Updates the python scripts in tools/{trace_processor, traceconv, tracebox}
 | |
| 
 | |
| This script does the following, for each entry in SCRIPTS_TO_UPDATE:
 | |
|   - Downloads the artifact by the LUCI infrastructure, one for each arch.
 | |
|   - Computes the SHA-256 of each artifact.
 | |
|   - Generates a manifest with URL, SHA-256 and other details.
 | |
|   - Merges get_perfetto_prebuilt.py with the manifest and writes tools/xxx.
 | |
| 
 | |
| This script is supposed to be run by Perfetto OWNERS after every monthly release
 | |
| after the LUCI jobs have completed.
 | |
| """
 | |
| 
 | |
| import argparse
 | |
| import hashlib
 | |
| import logging
 | |
| import os
 | |
| import subprocess
 | |
| import sys
 | |
| 
 | |
| from concurrent.futures import ThreadPoolExecutor
 | |
| 
 | |
| GCS_URL = 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts'
 | |
| 
 | |
| ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 | |
| TOOLS_DIR = os.path.join(ROOT_DIR, 'tools')
 | |
| 
 | |
| SCRIPTS_TO_UPDATE = [
 | |
|     {
 | |
|         'script':
 | |
|             'trace_processor',
 | |
|         'tool':
 | |
|             'trace_processor_shell',
 | |
|         'archs': [
 | |
|             'mac-amd64', 'mac-arm64', 'windows-amd64', 'linux-amd64',
 | |
|             'linux-arm', 'linux-arm64'
 | |
|         ]
 | |
|     },
 | |
|     {
 | |
|         'script': 'traceconv',
 | |
|         'tool': 'trace_to_text',
 | |
|         'archs': ['mac-amd64', 'mac-arm64', 'linux-amd64', 'windows-amd64']
 | |
|     },
 | |
|     {
 | |
|         'script':
 | |
|             'tracebox',
 | |
|         'tool':
 | |
|             'tracebox',
 | |
|         'archs': [
 | |
|             'mac-amd64', 'mac-arm64', 'linux-amd64', 'linux-amd64', 'linux-arm',
 | |
|             'linux-arm64'
 | |
|         ]
 | |
|     },
 | |
|     {
 | |
|         'script': 'heap_profile',
 | |
|         'tool': 'trace_to_text',
 | |
|         'archs': ['mac-amd64', 'mac-arm64', 'windows-amd64', 'linux-amd64']
 | |
|     },
 | |
|     {
 | |
|         'script': 'cpu_profile',
 | |
|         'tool': 'trace_to_text',
 | |
|         'archs': ['mac-amd64', 'windows-amd64', 'linux-amd64']
 | |
|     },
 | |
|     {
 | |
|         'script': 'record_android_trace',
 | |
|         'tool': 'tracebox',
 | |
|         'archs': ['android-arm', 'android-arm64', 'android-x86', 'android-x64']
 | |
|     },
 | |
| ]
 | |
| 
 | |
| # Maps a 'os-arch' string (were arch follows LUCI conventions) into
 | |
| # corresponding tuples that match against python's platform / machine API
 | |
| # (see get_perfetto_prebuilt.py for usage).
 | |
| ARCH_TO_PYTHON = {
 | |
|     'mac-amd64': {
 | |
|         'platform': 'darwin',
 | |
|         'machine': ['x86_64'],
 | |
|     },
 | |
|     'mac-arm64': {
 | |
|         'platform': 'darwin',
 | |
|         'machine': ['arm64'],
 | |
|     },
 | |
|     'windows-amd64': {
 | |
|         'platform': 'win32',
 | |
|         'machine': ['amd64'],
 | |
|     },
 | |
|     'linux-amd64': {
 | |
|         'platform': 'linux',
 | |
|         'machine': ['x86_64'],
 | |
|     },
 | |
|     'linux-arm': {
 | |
|         'platform': 'linux',
 | |
|         'machine': ['armv6l', 'armv7l', 'armv8l'],
 | |
|     },
 | |
|     'linux-arm64': {
 | |
|         'platform': 'linux',
 | |
|         'machine': ['aarch64'],
 | |
|     },
 | |
| }
 | |
| 
 | |
| 
 | |
| def make_manifest(git_revision, tool, arch):
 | |
|   ext = '.exe' if arch.startswith('windows') else ''
 | |
|   file_name = tool + ext
 | |
|   url = '%s/%s/%s/%s' % (GCS_URL, git_revision, arch, file_name)
 | |
|   logging.info('Downloading %s', url)
 | |
|   data = subprocess.check_output(['curl', '-fsL', '-o', '-', url])
 | |
|   manifest = {
 | |
|       'tool': tool,
 | |
|       'arch': arch,
 | |
|       'file_name': file_name,
 | |
|       'file_size': len(data),
 | |
|       'url': url,
 | |
|       'sha256': hashlib.sha256(data).hexdigest()
 | |
|   }
 | |
|   manifest.update(ARCH_TO_PYTHON.get(arch, {}))
 | |
|   return manifest
 | |
| 
 | |
| 
 | |
| # Returns the section of get_perfetto_prebuilt.py which should be copy/pasted
 | |
| # in the various scripts.
 | |
| def read_get_perfetto_prebuilt_script():
 | |
|   in_file = os.path.join(TOOLS_DIR, 'get_perfetto_prebuilt.py')
 | |
|   with open(in_file, 'r') as f:
 | |
|     contents = f.read()
 | |
|   return contents.split('COPIED_SECTION_START_MARKER')[1]
 | |
| 
 | |
| 
 | |
| def update_script(git_revision, tool_name, script_name, archs):
 | |
|   with ThreadPoolExecutor(max_workers=8) as executor:
 | |
|     manifests = list(
 | |
|         executor.map(lambda arch: make_manifest(git_revision, tool_name, arch),
 | |
|                      archs))
 | |
|   out_file = os.path.join(TOOLS_DIR, script_name)
 | |
|   with open(out_file) as f:
 | |
|     script = f.read()
 | |
| 
 | |
|   begin_marker = '\n# BEGIN_SECTION_GENERATED_BY(roll-prebuilts)\n'
 | |
|   end_marker = '\n# END_SECTION_GENERATED_BY(roll-prebuilts)\n'
 | |
|   before = script.partition(begin_marker)[0]
 | |
|   after = script.partition(end_marker)[2]
 | |
| 
 | |
|   content = '# Revision: {git_revision}\n'
 | |
|   content += 'PERFETTO_PREBUILT_MANIFEST = {manifests}\n'
 | |
|   content += '{fn_body}\n'
 | |
|   content = content.format(
 | |
|       git_revision=git_revision,
 | |
|       manifests=str(manifests),
 | |
|       fn_body=read_get_perfetto_prebuilt_script())
 | |
| 
 | |
|   script = before + begin_marker + content + end_marker + after
 | |
| 
 | |
|   with open(out_file + '.tmp', 'w') as f:
 | |
|     f.write(script)
 | |
|   subprocess.check_call(['yapf', '-i', out_file + '.tmp'])
 | |
|   os.rename(out_file + '.tmp', out_file)
 | |
|   os.chmod(out_file, 0o755)
 | |
| 
 | |
| 
 | |
| def main():
 | |
|   usage = '%s v20.0 | 0a1b2c3d\n\n' % __file__
 | |
|   usage += 'To list available revisions run\n'
 | |
|   usage += 'gsutil ls gs://perfetto-luci-artifacts/\n'
 | |
|   usage += 'or visit https://chrome-infra-packages.appspot.com/p/perfetto\n'
 | |
|   parser = argparse.ArgumentParser(usage=usage)
 | |
|   parser.add_argument('version')
 | |
|   args = parser.parse_args()
 | |
| 
 | |
|   git_revision = args.version
 | |
|   for spec in SCRIPTS_TO_UPDATE:
 | |
|     logging.info('Updating %s', spec['script'])
 | |
|     update_script(git_revision, spec['tool'], spec['script'], spec['archs'])
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|   logging.basicConfig(level=logging.INFO)
 | |
|   sys.exit(main())
 |