blob: 99f3459d8ad9cbad38fbb835c551bbad22cb1fd9 [file] [log] [blame]
Primiano Tucci1fe41c82021-07-28 20:52:40 +01001# Copyright (C) 2021 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""
15This source defines a self-contained function to fetch a perfetto prebuilt.
16
17This function is copy/pasted by //tools/roll-prebuilts in different places:
18- Into the //tools/{trace_processor, traceconv} scripts, which are just plain
19 wrappers around executables.
20- Into the //tools/{heap_profiler, record_android_trace} scripts, which contain
21 some other hand-written python code.
22In both cases toll-prebuilts copies this source (together with a manifest) into
23a section annotated with "BEGIN_SECTION_GENERATED_BY(roll-prebuilts)" / END... .
24The automated-copy-paste is to keep those script hermetic, so people can just
25download and run them without checking out the repo.
26
27The manifest argument looks as follows in the generated files:
28PERFETTO_PREBUILT_MANIFEST = [{
29 'tool': 'trace_to_text',
30 'arch': 'mac-amd64',
31 'file_name': 'trace_to_text',
32 'file_size': 7087080,
33 'url': https://commondatastorage.googleapis.com/.../trace_to_text',
34 'sha256': 7d957c005b0dc130f5bd855d6cec27e060d38841b320d04840afc569f9087490',
35 'platform': 'darwin',
36 'machine': 'x86_64'
37 },
38 ...
39]
40
41The intended usage is:
42
43bin_path = get_perfetto_prebuilt('trace_processor_shell')
44subprocess.call(bin_path, ...)
45"""
46
47from logging import exception
48
49PERFETTO_PREBUILT_MANIFEST = []
50
51# COPIED_SECTION_START_MARKER
52
53
54# DO NOT EDIT. If you wish to make edits to this code, you need to change only
55# //tools/get_perfetto_prebuilt.py and run /tools/roll-prebuilts to regenerate
56# all the others scripts this is embedded into.
57def get_perfetto_prebuilt(tool_name, soft_fail=False, arch=None):
58 """ Downloads the prebuilt, if necessary, and returns its path on disk. """
59
60 # The first time this is invoked, it downloads the |url| and caches it into
61 # ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
62 # cached version.
63 def download_or_get_cached(file_name, url, sha256):
64 import os, hashlib, subprocess
65 dir = os.path.join(
66 os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
67 os.makedirs(dir, exist_ok=True)
68 bin_path = os.path.join(dir, file_name)
69 sha256_path = os.path.join(dir, file_name + '.sha256')
70 needs_download = True
71
72 # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
73 # download is cached into file_name.sha256, just check if that matches.
74 if os.path.exists(bin_path) and os.path.exists(sha256_path):
75 with open(sha256_path, 'rb') as f:
76 digest = f.read().decode()
77 if digest == sha256:
78 needs_download = False
79
80 if needs_download:
81 # Either the filed doesn't exist or the SHA256 doesn't match.
82 tmp_path = bin_path + '.tmp'
83 print('Downloading ' + url)
84 subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
85 with open(tmp_path, 'rb') as fd:
86 actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
87 if actual_sha256 != sha256:
88 raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
89 (url, actual_sha256, sha256))
90 os.chmod(tmp_path, 0o755)
91 os.rename(tmp_path, bin_path)
92 with open(sha256_path, 'w') as f:
93 f.write(sha256)
94 return bin_path
95 # --- end of download_or_get_cached() ---
96
97 # --- get_perfetto_prebuilt() function starts here. ---
98 import os, platform, sys
99 plat = sys.platform.lower()
100 machine = platform.machine().lower()
101 manifest_entry = None
102 for entry in PERFETTO_PREBUILT_MANIFEST:
103 # If the caller overrides the arch, just match that (for Android prebuilts).
104 if arch and entry.get('arch') == arch:
105 manifest_entry = entry
106 break
107 # Otherwise guess the local machine arch.
108 if entry.get('tool') == tool_name and entry.get(
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100109 'platform') == plat and machine in entry.get('machine', []):
Primiano Tucci1fe41c82021-07-28 20:52:40 +0100110 manifest_entry = entry
111 break
112 if manifest_entry is None:
113 if soft_fail:
114 return None
115 raise Exception(
116 ('No prebuilts available for %s-%s\n' % (plat, machine)) +
117 'See https://perfetto.dev/docs/contributing/build-instructions')
118
119 return download_or_get_cached(
120 file_name=manifest_entry['file_name'],
121 url=manifest_entry['url'],
122 sha256=manifest_entry['sha256'])