xref: /aosp_15_r20/build/make/tools/perf/benchmarks (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project
3*9e94795aSAndroid Build Coastguard Worker#
4*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*9e94795aSAndroid Build Coastguard Worker#
8*9e94795aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*9e94795aSAndroid Build Coastguard Worker#
10*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*9e94795aSAndroid Build Coastguard Worker# limitations under the License.
15*9e94795aSAndroid Build Coastguard Worker
16*9e94795aSAndroid Build Coastguard Workerimport sys
17*9e94795aSAndroid Build Coastguard Workerif __name__ == "__main__":
18*9e94795aSAndroid Build Coastguard Worker    sys.dont_write_bytecode = True
19*9e94795aSAndroid Build Coastguard Worker
20*9e94795aSAndroid Build Coastguard Workerimport argparse
21*9e94795aSAndroid Build Coastguard Workerimport dataclasses
22*9e94795aSAndroid Build Coastguard Workerimport datetime
23*9e94795aSAndroid Build Coastguard Workerimport json
24*9e94795aSAndroid Build Coastguard Workerimport os
25*9e94795aSAndroid Build Coastguard Workerimport pathlib
26*9e94795aSAndroid Build Coastguard Workerimport random
27*9e94795aSAndroid Build Coastguard Workerimport re
28*9e94795aSAndroid Build Coastguard Workerimport shutil
29*9e94795aSAndroid Build Coastguard Workerimport subprocess
30*9e94795aSAndroid Build Coastguard Workerimport time
31*9e94795aSAndroid Build Coastguard Workerimport uuid
32*9e94795aSAndroid Build Coastguard Workerfrom typing import Optional
33*9e94795aSAndroid Build Coastguard Worker
34*9e94795aSAndroid Build Coastguard Workerimport pretty
35*9e94795aSAndroid Build Coastguard Workerimport utils
36*9e94795aSAndroid Build Coastguard Worker
37*9e94795aSAndroid Build Coastguard Worker
38*9e94795aSAndroid Build Coastguard Workerclass FatalError(Exception):
39*9e94795aSAndroid Build Coastguard Worker    def __init__(self):
40*9e94795aSAndroid Build Coastguard Worker        pass
41*9e94795aSAndroid Build Coastguard Worker
42*9e94795aSAndroid Build Coastguard Worker
43*9e94795aSAndroid Build Coastguard Workerclass OptionsError(Exception):
44*9e94795aSAndroid Build Coastguard Worker    def __init__(self, message):
45*9e94795aSAndroid Build Coastguard Worker        self.message = message
46*9e94795aSAndroid Build Coastguard Worker
47*9e94795aSAndroid Build Coastguard Worker
48*9e94795aSAndroid Build Coastguard Worker@dataclasses.dataclass(frozen=True)
49*9e94795aSAndroid Build Coastguard Workerclass Lunch:
50*9e94795aSAndroid Build Coastguard Worker    "Lunch combination"
51*9e94795aSAndroid Build Coastguard Worker
52*9e94795aSAndroid Build Coastguard Worker    target_product: str
53*9e94795aSAndroid Build Coastguard Worker    "TARGET_PRODUCT"
54*9e94795aSAndroid Build Coastguard Worker
55*9e94795aSAndroid Build Coastguard Worker    target_release: str
56*9e94795aSAndroid Build Coastguard Worker    "TARGET_RELEASE"
57*9e94795aSAndroid Build Coastguard Worker
58*9e94795aSAndroid Build Coastguard Worker    target_build_variant: str
59*9e94795aSAndroid Build Coastguard Worker    "TARGET_BUILD_VARIANT"
60*9e94795aSAndroid Build Coastguard Worker
61*9e94795aSAndroid Build Coastguard Worker    def ToDict(self):
62*9e94795aSAndroid Build Coastguard Worker        return {
63*9e94795aSAndroid Build Coastguard Worker            "TARGET_PRODUCT": self.target_product,
64*9e94795aSAndroid Build Coastguard Worker            "TARGET_RELEASE": self.target_release,
65*9e94795aSAndroid Build Coastguard Worker            "TARGET_BUILD_VARIANT": self.target_build_variant,
66*9e94795aSAndroid Build Coastguard Worker        }
67*9e94795aSAndroid Build Coastguard Worker
68*9e94795aSAndroid Build Coastguard Worker    def Combine(self):
69*9e94795aSAndroid Build Coastguard Worker        return f"{self.target_product}-{self.target_release}-{self.target_build_variant}"
70*9e94795aSAndroid Build Coastguard Worker
71*9e94795aSAndroid Build Coastguard Worker
72*9e94795aSAndroid Build Coastguard Worker@dataclasses.dataclass(frozen=True)
73*9e94795aSAndroid Build Coastguard Workerclass Change:
74*9e94795aSAndroid Build Coastguard Worker    "A change that we make to the tree, and how to undo it"
75*9e94795aSAndroid Build Coastguard Worker    label: str
76*9e94795aSAndroid Build Coastguard Worker    "String to print in the log when the change is made"
77*9e94795aSAndroid Build Coastguard Worker
78*9e94795aSAndroid Build Coastguard Worker    change: callable
79*9e94795aSAndroid Build Coastguard Worker    "Function to change the source tree"
80*9e94795aSAndroid Build Coastguard Worker
81*9e94795aSAndroid Build Coastguard Worker    undo: callable
82*9e94795aSAndroid Build Coastguard Worker    "Function to revert the source tree to its previous condition in the most minimal way possible."
83*9e94795aSAndroid Build Coastguard Worker
84*9e94795aSAndroid Build Coastguard Worker_DUMPVARS_VARS=[
85*9e94795aSAndroid Build Coastguard Worker    "COMMON_LUNCH_CHOICES",
86*9e94795aSAndroid Build Coastguard Worker    "HOST_PREBUILT_TAG",
87*9e94795aSAndroid Build Coastguard Worker    "print",
88*9e94795aSAndroid Build Coastguard Worker    "PRODUCT_OUT",
89*9e94795aSAndroid Build Coastguard Worker    "report_config",
90*9e94795aSAndroid Build Coastguard Worker    "TARGET_ARCH",
91*9e94795aSAndroid Build Coastguard Worker    "TARGET_BUILD_VARIANT",
92*9e94795aSAndroid Build Coastguard Worker    "TARGET_DEVICE",
93*9e94795aSAndroid Build Coastguard Worker    "TARGET_PRODUCT",
94*9e94795aSAndroid Build Coastguard Worker]
95*9e94795aSAndroid Build Coastguard Worker
96*9e94795aSAndroid Build Coastguard Worker_DUMPVARS_ABS_VARS =[
97*9e94795aSAndroid Build Coastguard Worker    "ANDROID_CLANG_PREBUILTS",
98*9e94795aSAndroid Build Coastguard Worker    "ANDROID_JAVA_HOME",
99*9e94795aSAndroid Build Coastguard Worker    "ANDROID_JAVA_TOOLCHAIN",
100*9e94795aSAndroid Build Coastguard Worker    "ANDROID_PREBUILTS",
101*9e94795aSAndroid Build Coastguard Worker    "HOST_OUT",
102*9e94795aSAndroid Build Coastguard Worker    "HOST_OUT_EXECUTABLES",
103*9e94795aSAndroid Build Coastguard Worker    "HOST_OUT_TESTCASES",
104*9e94795aSAndroid Build Coastguard Worker    "OUT_DIR",
105*9e94795aSAndroid Build Coastguard Worker    "print",
106*9e94795aSAndroid Build Coastguard Worker    "PRODUCT_OUT",
107*9e94795aSAndroid Build Coastguard Worker    "SOONG_HOST_OUT",
108*9e94795aSAndroid Build Coastguard Worker    "SOONG_HOST_OUT_EXECUTABLES",
109*9e94795aSAndroid Build Coastguard Worker    "TARGET_OUT_TESTCASES",
110*9e94795aSAndroid Build Coastguard Worker]
111*9e94795aSAndroid Build Coastguard Worker
112*9e94795aSAndroid Build Coastguard Worker@dataclasses.dataclass(frozen=True)
113*9e94795aSAndroid Build Coastguard Workerclass Benchmark:
114*9e94795aSAndroid Build Coastguard Worker    "Something we measure"
115*9e94795aSAndroid Build Coastguard Worker
116*9e94795aSAndroid Build Coastguard Worker    id: str
117*9e94795aSAndroid Build Coastguard Worker    "Short ID for the benchmark, for the command line"
118*9e94795aSAndroid Build Coastguard Worker
119*9e94795aSAndroid Build Coastguard Worker    title: str
120*9e94795aSAndroid Build Coastguard Worker    "Title for reports"
121*9e94795aSAndroid Build Coastguard Worker
122*9e94795aSAndroid Build Coastguard Worker    change: Change
123*9e94795aSAndroid Build Coastguard Worker    "Source tree modification for the benchmark that will be measured"
124*9e94795aSAndroid Build Coastguard Worker
125*9e94795aSAndroid Build Coastguard Worker    dumpvars: Optional[bool] = False
126*9e94795aSAndroid Build Coastguard Worker    "If specified, soong will run in dumpvars mode rather than build-mode."
127*9e94795aSAndroid Build Coastguard Worker
128*9e94795aSAndroid Build Coastguard Worker    modules: Optional[list[str]] = None
129*9e94795aSAndroid Build Coastguard Worker    "Build modules to build on soong command line"
130*9e94795aSAndroid Build Coastguard Worker
131*9e94795aSAndroid Build Coastguard Worker    preroll: Optional[int] = 0
132*9e94795aSAndroid Build Coastguard Worker    "Number of times to run the build command to stabilize"
133*9e94795aSAndroid Build Coastguard Worker
134*9e94795aSAndroid Build Coastguard Worker    postroll: Optional[int] = 3
135*9e94795aSAndroid Build Coastguard Worker    "Number of times to run the build command after reverting the action to stabilize"
136*9e94795aSAndroid Build Coastguard Worker
137*9e94795aSAndroid Build Coastguard Worker    def build_description(self):
138*9e94795aSAndroid Build Coastguard Worker      "Short description of the benchmark's Soong invocation."
139*9e94795aSAndroid Build Coastguard Worker      if self.dumpvars:
140*9e94795aSAndroid Build Coastguard Worker        return "dumpvars"
141*9e94795aSAndroid Build Coastguard Worker      elif self.modules:
142*9e94795aSAndroid Build Coastguard Worker        return " ".join(self.modules)
143*9e94795aSAndroid Build Coastguard Worker      return ""
144*9e94795aSAndroid Build Coastguard Worker
145*9e94795aSAndroid Build Coastguard Worker
146*9e94795aSAndroid Build Coastguard Worker    def soong_command(self, root):
147*9e94795aSAndroid Build Coastguard Worker      "Command line args to soong_ui for this benchmark."
148*9e94795aSAndroid Build Coastguard Worker      if self.dumpvars:
149*9e94795aSAndroid Build Coastguard Worker          return [
150*9e94795aSAndroid Build Coastguard Worker              "--dumpvars-mode",
151*9e94795aSAndroid Build Coastguard Worker              f"--vars=\"{' '.join(_DUMPVARS_VARS)}\"",
152*9e94795aSAndroid Build Coastguard Worker              f"--abs-vars=\"{' '.join(_DUMPVARS_ABS_VARS)}\"",
153*9e94795aSAndroid Build Coastguard Worker              "--var-prefix=var_cache_",
154*9e94795aSAndroid Build Coastguard Worker              "--abs-var-prefix=abs_var_cache_",
155*9e94795aSAndroid Build Coastguard Worker          ]
156*9e94795aSAndroid Build Coastguard Worker      elif self.modules:
157*9e94795aSAndroid Build Coastguard Worker          return [
158*9e94795aSAndroid Build Coastguard Worker              "--build-mode",
159*9e94795aSAndroid Build Coastguard Worker              "--all-modules",
160*9e94795aSAndroid Build Coastguard Worker              f"--dir={root}",
161*9e94795aSAndroid Build Coastguard Worker              "--skip-metrics-upload",
162*9e94795aSAndroid Build Coastguard Worker          ] + self.modules
163*9e94795aSAndroid Build Coastguard Worker      else:
164*9e94795aSAndroid Build Coastguard Worker          raise Exception("Benchmark must specify dumpvars or modules")
165*9e94795aSAndroid Build Coastguard Worker
166*9e94795aSAndroid Build Coastguard Worker
167*9e94795aSAndroid Build Coastguard Worker@dataclasses.dataclass(frozen=True)
168*9e94795aSAndroid Build Coastguard Workerclass FileSnapshot:
169*9e94795aSAndroid Build Coastguard Worker    "Snapshot of a file's contents."
170*9e94795aSAndroid Build Coastguard Worker
171*9e94795aSAndroid Build Coastguard Worker    filename: str
172*9e94795aSAndroid Build Coastguard Worker    "The file that was snapshottened"
173*9e94795aSAndroid Build Coastguard Worker
174*9e94795aSAndroid Build Coastguard Worker    contents: str
175*9e94795aSAndroid Build Coastguard Worker    "The contents of the file"
176*9e94795aSAndroid Build Coastguard Worker
177*9e94795aSAndroid Build Coastguard Worker    def write(self):
178*9e94795aSAndroid Build Coastguard Worker        "Write the contents back to the file"
179*9e94795aSAndroid Build Coastguard Worker        with open(self.filename, "w") as f:
180*9e94795aSAndroid Build Coastguard Worker            f.write(self.contents)
181*9e94795aSAndroid Build Coastguard Worker
182*9e94795aSAndroid Build Coastguard Worker
183*9e94795aSAndroid Build Coastguard Workerdef Snapshot(filename):
184*9e94795aSAndroid Build Coastguard Worker    """Return a FileSnapshot with the file's current contents."""
185*9e94795aSAndroid Build Coastguard Worker    with open(filename) as f:
186*9e94795aSAndroid Build Coastguard Worker        contents = f.read()
187*9e94795aSAndroid Build Coastguard Worker    return FileSnapshot(filename, contents)
188*9e94795aSAndroid Build Coastguard Worker
189*9e94795aSAndroid Build Coastguard Worker
190*9e94795aSAndroid Build Coastguard Workerdef Clean():
191*9e94795aSAndroid Build Coastguard Worker    """Remove the out directory."""
192*9e94795aSAndroid Build Coastguard Worker    def remove_out():
193*9e94795aSAndroid Build Coastguard Worker        out_dir = utils.get_out_dir()
194*9e94795aSAndroid Build Coastguard Worker        #only remove actual contents, in case out is a symlink (as is the case for cog)
195*9e94795aSAndroid Build Coastguard Worker        if os.path.exists(out_dir):
196*9e94795aSAndroid Build Coastguard Worker          for filename in os.listdir(out_dir):
197*9e94795aSAndroid Build Coastguard Worker              p = os.path.join(out_dir, filename)
198*9e94795aSAndroid Build Coastguard Worker              if os.path.isfile(p) or os.path.islink(p):
199*9e94795aSAndroid Build Coastguard Worker                  os.remove(p)
200*9e94795aSAndroid Build Coastguard Worker              elif os.path.isdir(p):
201*9e94795aSAndroid Build Coastguard Worker                  shutil.rmtree(p)
202*9e94795aSAndroid Build Coastguard Worker    return Change(label="Remove out", change=remove_out, undo=lambda: None)
203*9e94795aSAndroid Build Coastguard Worker
204*9e94795aSAndroid Build Coastguard Worker
205*9e94795aSAndroid Build Coastguard Workerdef NoChange():
206*9e94795aSAndroid Build Coastguard Worker    """No change to the source tree."""
207*9e94795aSAndroid Build Coastguard Worker    return Change(label="No change", change=lambda: None, undo=lambda: None)
208*9e94795aSAndroid Build Coastguard Worker
209*9e94795aSAndroid Build Coastguard Worker
210*9e94795aSAndroid Build Coastguard Workerdef Create(filename):
211*9e94795aSAndroid Build Coastguard Worker    "Create an action to create `filename`. The parent directory must exist."
212*9e94795aSAndroid Build Coastguard Worker    def create():
213*9e94795aSAndroid Build Coastguard Worker        with open(filename, "w") as f:
214*9e94795aSAndroid Build Coastguard Worker            pass
215*9e94795aSAndroid Build Coastguard Worker    def delete():
216*9e94795aSAndroid Build Coastguard Worker        os.remove(filename)
217*9e94795aSAndroid Build Coastguard Worker    return Change(
218*9e94795aSAndroid Build Coastguard Worker                label=f"Create {filename}",
219*9e94795aSAndroid Build Coastguard Worker                change=create,
220*9e94795aSAndroid Build Coastguard Worker                undo=delete,
221*9e94795aSAndroid Build Coastguard Worker            )
222*9e94795aSAndroid Build Coastguard Worker
223*9e94795aSAndroid Build Coastguard Worker
224*9e94795aSAndroid Build Coastguard Workerdef Modify(filename, contents, before=None):
225*9e94795aSAndroid Build Coastguard Worker    """Create an action to modify `filename` by appending the result of `contents`
226*9e94795aSAndroid Build Coastguard Worker    before the last instances of `before` in the file.
227*9e94795aSAndroid Build Coastguard Worker
228*9e94795aSAndroid Build Coastguard Worker    Raises an error if `before` doesn't appear in the file.
229*9e94795aSAndroid Build Coastguard Worker    """
230*9e94795aSAndroid Build Coastguard Worker    orig = Snapshot(filename)
231*9e94795aSAndroid Build Coastguard Worker    if before:
232*9e94795aSAndroid Build Coastguard Worker        index = orig.contents.rfind(before)
233*9e94795aSAndroid Build Coastguard Worker        if index < 0:
234*9e94795aSAndroid Build Coastguard Worker            report_error(f"{filename}: Unable to find string '{before}' for modify operation.")
235*9e94795aSAndroid Build Coastguard Worker            raise FatalError()
236*9e94795aSAndroid Build Coastguard Worker    else:
237*9e94795aSAndroid Build Coastguard Worker        index = len(orig.contents)
238*9e94795aSAndroid Build Coastguard Worker    modified = FileSnapshot(filename, orig.contents[:index] + contents() + orig.contents[index:])
239*9e94795aSAndroid Build Coastguard Worker    if False:
240*9e94795aSAndroid Build Coastguard Worker        print(f"Modify: {filename}")
241*9e94795aSAndroid Build Coastguard Worker        x = orig.contents.replace("\n", "\n   ORIG")
242*9e94795aSAndroid Build Coastguard Worker        print(f"   ORIG {x}")
243*9e94795aSAndroid Build Coastguard Worker        x = modified.contents.replace("\n", "\n   MODIFIED")
244*9e94795aSAndroid Build Coastguard Worker        print(f"   MODIFIED {x}")
245*9e94795aSAndroid Build Coastguard Worker
246*9e94795aSAndroid Build Coastguard Worker    return Change(
247*9e94795aSAndroid Build Coastguard Worker            label="Modify " + filename,
248*9e94795aSAndroid Build Coastguard Worker            change=lambda: modified.write(),
249*9e94795aSAndroid Build Coastguard Worker            undo=lambda: orig.write()
250*9e94795aSAndroid Build Coastguard Worker        )
251*9e94795aSAndroid Build Coastguard Worker
252*9e94795aSAndroid Build Coastguard Workerdef ChangePublicApi():
253*9e94795aSAndroid Build Coastguard Worker    change = AddJavaField("frameworks/base/core/java/android/provider/Settings.java",
254*9e94795aSAndroid Build Coastguard Worker                 "@android.annotation.SuppressLint(\"UnflaggedApi\") public")
255*9e94795aSAndroid Build Coastguard Worker    orig_current_text = Snapshot("frameworks/base/core/api/current.txt")
256*9e94795aSAndroid Build Coastguard Worker
257*9e94795aSAndroid Build Coastguard Worker    def undo():
258*9e94795aSAndroid Build Coastguard Worker        change.undo()
259*9e94795aSAndroid Build Coastguard Worker        orig_current_text.write()
260*9e94795aSAndroid Build Coastguard Worker
261*9e94795aSAndroid Build Coastguard Worker    return Change(
262*9e94795aSAndroid Build Coastguard Worker        label=change.label,
263*9e94795aSAndroid Build Coastguard Worker        change=change.change,
264*9e94795aSAndroid Build Coastguard Worker        undo=lambda: undo()
265*9e94795aSAndroid Build Coastguard Worker    )
266*9e94795aSAndroid Build Coastguard Worker
267*9e94795aSAndroid Build Coastguard Workerdef AddJavaField(filename, prefix):
268*9e94795aSAndroid Build Coastguard Worker    return Modify(filename,
269*9e94795aSAndroid Build Coastguard Worker                  lambda: f"{prefix} static final int BENCHMARK = {random.randint(0, 1000000)};\n",
270*9e94795aSAndroid Build Coastguard Worker                  before="}")
271*9e94795aSAndroid Build Coastguard Worker
272*9e94795aSAndroid Build Coastguard Worker
273*9e94795aSAndroid Build Coastguard Workerdef Comment(prefix, suffix=""):
274*9e94795aSAndroid Build Coastguard Worker    return lambda: prefix + " " + str(uuid.uuid4()) + suffix
275*9e94795aSAndroid Build Coastguard Worker
276*9e94795aSAndroid Build Coastguard Worker
277*9e94795aSAndroid Build Coastguard Workerclass BenchmarkReport():
278*9e94795aSAndroid Build Coastguard Worker    "Information about a run of the benchmark"
279*9e94795aSAndroid Build Coastguard Worker
280*9e94795aSAndroid Build Coastguard Worker    lunch: Lunch
281*9e94795aSAndroid Build Coastguard Worker    "lunch combo"
282*9e94795aSAndroid Build Coastguard Worker
283*9e94795aSAndroid Build Coastguard Worker    benchmark: Benchmark
284*9e94795aSAndroid Build Coastguard Worker    "The benchmark object."
285*9e94795aSAndroid Build Coastguard Worker
286*9e94795aSAndroid Build Coastguard Worker    iteration: int
287*9e94795aSAndroid Build Coastguard Worker    "Which iteration of the benchmark"
288*9e94795aSAndroid Build Coastguard Worker
289*9e94795aSAndroid Build Coastguard Worker    log_dir: str
290*9e94795aSAndroid Build Coastguard Worker    "Path the the log directory, relative to the root of the reports directory"
291*9e94795aSAndroid Build Coastguard Worker
292*9e94795aSAndroid Build Coastguard Worker    preroll_duration_ns: [int]
293*9e94795aSAndroid Build Coastguard Worker    "Durations of the in nanoseconds."
294*9e94795aSAndroid Build Coastguard Worker
295*9e94795aSAndroid Build Coastguard Worker    duration_ns: int
296*9e94795aSAndroid Build Coastguard Worker    "Duration of the measured portion of the benchmark in nanoseconds."
297*9e94795aSAndroid Build Coastguard Worker
298*9e94795aSAndroid Build Coastguard Worker    postroll_duration_ns: [int]
299*9e94795aSAndroid Build Coastguard Worker    "Durations of the postrolls in nanoseconds."
300*9e94795aSAndroid Build Coastguard Worker
301*9e94795aSAndroid Build Coastguard Worker    complete: bool
302*9e94795aSAndroid Build Coastguard Worker    "Whether the benchmark made it all the way through the postrolls."
303*9e94795aSAndroid Build Coastguard Worker
304*9e94795aSAndroid Build Coastguard Worker    def __init__(self, lunch, benchmark, iteration, log_dir):
305*9e94795aSAndroid Build Coastguard Worker        self.lunch = lunch
306*9e94795aSAndroid Build Coastguard Worker        self.benchmark = benchmark
307*9e94795aSAndroid Build Coastguard Worker        self.iteration = iteration
308*9e94795aSAndroid Build Coastguard Worker        self.log_dir = log_dir
309*9e94795aSAndroid Build Coastguard Worker        self.preroll_duration_ns = []
310*9e94795aSAndroid Build Coastguard Worker        self.duration_ns = -1
311*9e94795aSAndroid Build Coastguard Worker        self.postroll_duration_ns = []
312*9e94795aSAndroid Build Coastguard Worker        self.complete = False
313*9e94795aSAndroid Build Coastguard Worker
314*9e94795aSAndroid Build Coastguard Worker    def ToDict(self):
315*9e94795aSAndroid Build Coastguard Worker        return {
316*9e94795aSAndroid Build Coastguard Worker            "lunch": self.lunch.ToDict(),
317*9e94795aSAndroid Build Coastguard Worker            "id": self.benchmark.id,
318*9e94795aSAndroid Build Coastguard Worker            "title": self.benchmark.title,
319*9e94795aSAndroid Build Coastguard Worker            "modules": self.benchmark.modules,
320*9e94795aSAndroid Build Coastguard Worker            "dumpvars": self.benchmark.dumpvars,
321*9e94795aSAndroid Build Coastguard Worker            "change": self.benchmark.change.label,
322*9e94795aSAndroid Build Coastguard Worker            "iteration": self.iteration,
323*9e94795aSAndroid Build Coastguard Worker            "log_dir": self.log_dir,
324*9e94795aSAndroid Build Coastguard Worker            "preroll_duration_ns": self.preroll_duration_ns,
325*9e94795aSAndroid Build Coastguard Worker            "duration_ns": self.duration_ns,
326*9e94795aSAndroid Build Coastguard Worker            "postroll_duration_ns": self.postroll_duration_ns,
327*9e94795aSAndroid Build Coastguard Worker            "complete": self.complete,
328*9e94795aSAndroid Build Coastguard Worker        }
329*9e94795aSAndroid Build Coastguard Worker
330*9e94795aSAndroid Build Coastguard Workerclass Runner():
331*9e94795aSAndroid Build Coastguard Worker    """Runs the benchmarks."""
332*9e94795aSAndroid Build Coastguard Worker
333*9e94795aSAndroid Build Coastguard Worker    def __init__(self, options):
334*9e94795aSAndroid Build Coastguard Worker        self._options = options
335*9e94795aSAndroid Build Coastguard Worker        self._reports = []
336*9e94795aSAndroid Build Coastguard Worker        self._complete = False
337*9e94795aSAndroid Build Coastguard Worker
338*9e94795aSAndroid Build Coastguard Worker    def Run(self):
339*9e94795aSAndroid Build Coastguard Worker        """Run all of the user-selected benchmarks."""
340*9e94795aSAndroid Build Coastguard Worker        # Clean out the log dir or create it if necessary
341*9e94795aSAndroid Build Coastguard Worker        prepare_log_dir(self._options.LogDir())
342*9e94795aSAndroid Build Coastguard Worker
343*9e94795aSAndroid Build Coastguard Worker        try:
344*9e94795aSAndroid Build Coastguard Worker            for lunch in self._options.Lunches():
345*9e94795aSAndroid Build Coastguard Worker                print(lunch)
346*9e94795aSAndroid Build Coastguard Worker                for benchmark in self._options.Benchmarks():
347*9e94795aSAndroid Build Coastguard Worker                    for iteration in range(self._options.Iterations()):
348*9e94795aSAndroid Build Coastguard Worker                        self._run_benchmark(lunch, benchmark, iteration)
349*9e94795aSAndroid Build Coastguard Worker            self._complete = True
350*9e94795aSAndroid Build Coastguard Worker        finally:
351*9e94795aSAndroid Build Coastguard Worker            self._write_summary()
352*9e94795aSAndroid Build Coastguard Worker
353*9e94795aSAndroid Build Coastguard Worker
354*9e94795aSAndroid Build Coastguard Worker    def _run_benchmark(self, lunch, benchmark, iteration):
355*9e94795aSAndroid Build Coastguard Worker        """Run a single benchmark."""
356*9e94795aSAndroid Build Coastguard Worker        benchmark_log_subdir = self._benchmark_log_dir(lunch, benchmark, iteration)
357*9e94795aSAndroid Build Coastguard Worker        benchmark_log_dir = self._options.LogDir().joinpath(benchmark_log_subdir)
358*9e94795aSAndroid Build Coastguard Worker
359*9e94795aSAndroid Build Coastguard Worker        sys.stderr.write(f"STARTING BENCHMARK: {benchmark.id}\n")
360*9e94795aSAndroid Build Coastguard Worker        sys.stderr.write(f"             lunch: {lunch.Combine()}\n")
361*9e94795aSAndroid Build Coastguard Worker        sys.stderr.write(f"         iteration: {iteration}\n")
362*9e94795aSAndroid Build Coastguard Worker        sys.stderr.write(f" benchmark_log_dir: {benchmark_log_dir}\n")
363*9e94795aSAndroid Build Coastguard Worker
364*9e94795aSAndroid Build Coastguard Worker        report = BenchmarkReport(lunch, benchmark, iteration, benchmark_log_subdir)
365*9e94795aSAndroid Build Coastguard Worker        self._reports.append(report)
366*9e94795aSAndroid Build Coastguard Worker
367*9e94795aSAndroid Build Coastguard Worker        # Preroll builds
368*9e94795aSAndroid Build Coastguard Worker        for i in range(benchmark.preroll):
369*9e94795aSAndroid Build Coastguard Worker            ns = self._run_build(lunch, benchmark_log_dir.joinpath(f"pre_{i}"), benchmark)
370*9e94795aSAndroid Build Coastguard Worker            report.preroll_duration_ns.append(ns)
371*9e94795aSAndroid Build Coastguard Worker
372*9e94795aSAndroid Build Coastguard Worker        sys.stderr.write(f"PERFORMING CHANGE: {benchmark.change.label}\n")
373*9e94795aSAndroid Build Coastguard Worker        if not self._options.DryRun():
374*9e94795aSAndroid Build Coastguard Worker            benchmark.change.change()
375*9e94795aSAndroid Build Coastguard Worker        try:
376*9e94795aSAndroid Build Coastguard Worker
377*9e94795aSAndroid Build Coastguard Worker            # Measured build
378*9e94795aSAndroid Build Coastguard Worker            ns = self._run_build(lunch, benchmark_log_dir.joinpath("measured"), benchmark)
379*9e94795aSAndroid Build Coastguard Worker            report.duration_ns = ns
380*9e94795aSAndroid Build Coastguard Worker
381*9e94795aSAndroid Build Coastguard Worker            dist_one = self._options.DistOne()
382*9e94795aSAndroid Build Coastguard Worker            if dist_one:
383*9e94795aSAndroid Build Coastguard Worker                # If we're disting just one benchmark, save the logs and we can stop here.
384*9e94795aSAndroid Build Coastguard Worker                self._dist(utils.get_dist_dir(), benchmark.dumpvars)
385*9e94795aSAndroid Build Coastguard Worker            else:
386*9e94795aSAndroid Build Coastguard Worker                self._dist(benchmark_log_dir, benchmark.dumpvars, store_metrics_only=True)
387*9e94795aSAndroid Build Coastguard Worker                # Postroll builds
388*9e94795aSAndroid Build Coastguard Worker                for i in range(benchmark.postroll):
389*9e94795aSAndroid Build Coastguard Worker                    ns = self._run_build(lunch, benchmark_log_dir.joinpath(f"post_{i}"),
390*9e94795aSAndroid Build Coastguard Worker                                         benchmark)
391*9e94795aSAndroid Build Coastguard Worker                    report.postroll_duration_ns.append(ns)
392*9e94795aSAndroid Build Coastguard Worker
393*9e94795aSAndroid Build Coastguard Worker        finally:
394*9e94795aSAndroid Build Coastguard Worker            # Always undo, even if we crashed or the build failed and we stopped.
395*9e94795aSAndroid Build Coastguard Worker            sys.stderr.write(f"UNDOING CHANGE: {benchmark.change.label}\n")
396*9e94795aSAndroid Build Coastguard Worker            if not self._options.DryRun():
397*9e94795aSAndroid Build Coastguard Worker                benchmark.change.undo()
398*9e94795aSAndroid Build Coastguard Worker
399*9e94795aSAndroid Build Coastguard Worker        self._write_summary()
400*9e94795aSAndroid Build Coastguard Worker        sys.stderr.write(f"FINISHED BENCHMARK: {benchmark.id}\n")
401*9e94795aSAndroid Build Coastguard Worker
402*9e94795aSAndroid Build Coastguard Worker    def _benchmark_log_dir(self, lunch, benchmark, iteration):
403*9e94795aSAndroid Build Coastguard Worker        """Construct the log directory fir a benchmark run."""
404*9e94795aSAndroid Build Coastguard Worker        path = f"{lunch.Combine()}/{benchmark.id}"
405*9e94795aSAndroid Build Coastguard Worker        # Zero pad to the correct length for correct alpha sorting
406*9e94795aSAndroid Build Coastguard Worker        path += ("/%0" + str(len(str(self._options.Iterations()))) + "d") % iteration
407*9e94795aSAndroid Build Coastguard Worker        return path
408*9e94795aSAndroid Build Coastguard Worker
409*9e94795aSAndroid Build Coastguard Worker    def _run_build(self, lunch, build_log_dir, benchmark):
410*9e94795aSAndroid Build Coastguard Worker        """Builds the modules.  Saves interesting log files to log_dir.  Raises FatalError
411*9e94795aSAndroid Build Coastguard Worker        if the build fails.
412*9e94795aSAndroid Build Coastguard Worker        """
413*9e94795aSAndroid Build Coastguard Worker        sys.stderr.write(f"STARTING BUILD {benchmark.build_description()}\n")
414*9e94795aSAndroid Build Coastguard Worker
415*9e94795aSAndroid Build Coastguard Worker        before_ns = time.perf_counter_ns()
416*9e94795aSAndroid Build Coastguard Worker        if not self._options.DryRun():
417*9e94795aSAndroid Build Coastguard Worker            cmd = [
418*9e94795aSAndroid Build Coastguard Worker                "build/soong/soong_ui.bash",
419*9e94795aSAndroid Build Coastguard Worker            ] + benchmark.soong_command(self._options.root)
420*9e94795aSAndroid Build Coastguard Worker            env = dict(os.environ)
421*9e94795aSAndroid Build Coastguard Worker            env["TARGET_PRODUCT"] = lunch.target_product
422*9e94795aSAndroid Build Coastguard Worker            env["TARGET_RELEASE"] = lunch.target_release
423*9e94795aSAndroid Build Coastguard Worker            env["TARGET_BUILD_VARIANT"] = lunch.target_build_variant
424*9e94795aSAndroid Build Coastguard Worker            returncode = subprocess.call(cmd, env=env)
425*9e94795aSAndroid Build Coastguard Worker            if returncode != 0:
426*9e94795aSAndroid Build Coastguard Worker                report_error(f"Build failed: {' '.join(cmd)}")
427*9e94795aSAndroid Build Coastguard Worker                raise FatalError()
428*9e94795aSAndroid Build Coastguard Worker
429*9e94795aSAndroid Build Coastguard Worker        after_ns = time.perf_counter_ns()
430*9e94795aSAndroid Build Coastguard Worker
431*9e94795aSAndroid Build Coastguard Worker        # TODO: Copy some log files.
432*9e94795aSAndroid Build Coastguard Worker
433*9e94795aSAndroid Build Coastguard Worker        sys.stderr.write(f"FINISHED BUILD {benchmark.build_description()}\n")
434*9e94795aSAndroid Build Coastguard Worker
435*9e94795aSAndroid Build Coastguard Worker        return after_ns - before_ns
436*9e94795aSAndroid Build Coastguard Worker
437*9e94795aSAndroid Build Coastguard Worker    def _dist(self, dist_dir, dumpvars, store_metrics_only=False):
438*9e94795aSAndroid Build Coastguard Worker        out_dir = utils.get_out_dir()
439*9e94795aSAndroid Build Coastguard Worker        dest_dir = dist_dir.joinpath("logs")
440*9e94795aSAndroid Build Coastguard Worker        os.makedirs(dest_dir, exist_ok=True)
441*9e94795aSAndroid Build Coastguard Worker        basenames = [
442*9e94795aSAndroid Build Coastguard Worker            "soong_build_metrics.pb",
443*9e94795aSAndroid Build Coastguard Worker            "soong_metrics",
444*9e94795aSAndroid Build Coastguard Worker        ]
445*9e94795aSAndroid Build Coastguard Worker        if not store_metrics_only:
446*9e94795aSAndroid Build Coastguard Worker            basenames.extend([
447*9e94795aSAndroid Build Coastguard Worker                "build.trace.gz",
448*9e94795aSAndroid Build Coastguard Worker                "soong.log",
449*9e94795aSAndroid Build Coastguard Worker            ])
450*9e94795aSAndroid Build Coastguard Worker        if dumpvars:
451*9e94795aSAndroid Build Coastguard Worker            basenames = ['dumpvars-'+b for b in basenames]
452*9e94795aSAndroid Build Coastguard Worker        for base in basenames:
453*9e94795aSAndroid Build Coastguard Worker            src = out_dir.joinpath(base)
454*9e94795aSAndroid Build Coastguard Worker            if src.exists():
455*9e94795aSAndroid Build Coastguard Worker                sys.stderr.write(f"DIST: copied {src} to {dest_dir}\n")
456*9e94795aSAndroid Build Coastguard Worker                shutil.copy(src, dest_dir)
457*9e94795aSAndroid Build Coastguard Worker
458*9e94795aSAndroid Build Coastguard Worker    def _write_summary(self):
459*9e94795aSAndroid Build Coastguard Worker        # Write the results, even if the build failed or we crashed, including
460*9e94795aSAndroid Build Coastguard Worker        # whether we finished all of the benchmarks.
461*9e94795aSAndroid Build Coastguard Worker        data = {
462*9e94795aSAndroid Build Coastguard Worker            "start_time": self._options.Timestamp().isoformat(),
463*9e94795aSAndroid Build Coastguard Worker            "branch": self._options.Branch(),
464*9e94795aSAndroid Build Coastguard Worker            "tag": self._options.Tag(),
465*9e94795aSAndroid Build Coastguard Worker            "benchmarks": [report.ToDict() for report in self._reports],
466*9e94795aSAndroid Build Coastguard Worker            "complete": self._complete,
467*9e94795aSAndroid Build Coastguard Worker        }
468*9e94795aSAndroid Build Coastguard Worker        with open(self._options.LogDir().joinpath("summary.json"), "w", encoding="utf-8") as f:
469*9e94795aSAndroid Build Coastguard Worker            json.dump(data, f, indent=2, sort_keys=True)
470*9e94795aSAndroid Build Coastguard Worker
471*9e94795aSAndroid Build Coastguard Worker
472*9e94795aSAndroid Build Coastguard Workerdef benchmark_table(benchmarks):
473*9e94795aSAndroid Build Coastguard Worker    rows = [("ID", "DESCRIPTION", "REBUILD"),]
474*9e94795aSAndroid Build Coastguard Worker    rows += [(benchmark.id, benchmark.title, benchmark.build_description()) for benchmark in
475*9e94795aSAndroid Build Coastguard Worker             benchmarks]
476*9e94795aSAndroid Build Coastguard Worker    return rows
477*9e94795aSAndroid Build Coastguard Worker
478*9e94795aSAndroid Build Coastguard Worker
479*9e94795aSAndroid Build Coastguard Workerdef prepare_log_dir(directory):
480*9e94795aSAndroid Build Coastguard Worker    if os.path.exists(directory):
481*9e94795aSAndroid Build Coastguard Worker        # If it exists and isn't a directory, fail.
482*9e94795aSAndroid Build Coastguard Worker        if not os.path.isdir(directory):
483*9e94795aSAndroid Build Coastguard Worker            report_error(f"Log directory already exists but isn't a directory: {directory}")
484*9e94795aSAndroid Build Coastguard Worker            raise FatalError()
485*9e94795aSAndroid Build Coastguard Worker        # Make sure the directory is empty. Do this rather than deleting it to handle
486*9e94795aSAndroid Build Coastguard Worker        # symlinks cleanly.
487*9e94795aSAndroid Build Coastguard Worker        for filename in os.listdir(directory):
488*9e94795aSAndroid Build Coastguard Worker            entry = os.path.join(directory, filename)
489*9e94795aSAndroid Build Coastguard Worker            if os.path.isdir(entry):
490*9e94795aSAndroid Build Coastguard Worker                shutil.rmtree(entry)
491*9e94795aSAndroid Build Coastguard Worker            else:
492*9e94795aSAndroid Build Coastguard Worker                os.unlink(entry)
493*9e94795aSAndroid Build Coastguard Worker    else:
494*9e94795aSAndroid Build Coastguard Worker        # Create it
495*9e94795aSAndroid Build Coastguard Worker        os.makedirs(directory)
496*9e94795aSAndroid Build Coastguard Worker
497*9e94795aSAndroid Build Coastguard Worker
498*9e94795aSAndroid Build Coastguard Workerclass Options():
499*9e94795aSAndroid Build Coastguard Worker    def __init__(self):
500*9e94795aSAndroid Build Coastguard Worker        self._had_error = False
501*9e94795aSAndroid Build Coastguard Worker
502*9e94795aSAndroid Build Coastguard Worker        # Wall time clock when we started
503*9e94795aSAndroid Build Coastguard Worker        self._timestamp = datetime.datetime.now(datetime.timezone.utc)
504*9e94795aSAndroid Build Coastguard Worker
505*9e94795aSAndroid Build Coastguard Worker        # Move to the root of the tree right away. Everything must happen from there.
506*9e94795aSAndroid Build Coastguard Worker        self.root = utils.get_root()
507*9e94795aSAndroid Build Coastguard Worker        if not self.root:
508*9e94795aSAndroid Build Coastguard Worker            report_error("Unable to find root of tree from cwd.")
509*9e94795aSAndroid Build Coastguard Worker            raise FatalError()
510*9e94795aSAndroid Build Coastguard Worker        os.chdir(self.root)
511*9e94795aSAndroid Build Coastguard Worker
512*9e94795aSAndroid Build Coastguard Worker        # Initialize the Benchmarks. Note that this pre-loads all of the files, etc.
513*9e94795aSAndroid Build Coastguard Worker        # Doing all that here forces us to fail fast if one of them can't load a required
514*9e94795aSAndroid Build Coastguard Worker        # file, at the cost of a small startup speed. Don't make this do something slow
515*9e94795aSAndroid Build Coastguard Worker        # like scan the whole tree.
516*9e94795aSAndroid Build Coastguard Worker        self._init_benchmarks()
517*9e94795aSAndroid Build Coastguard Worker
518*9e94795aSAndroid Build Coastguard Worker        # Argument parsing
519*9e94795aSAndroid Build Coastguard Worker        epilog = f"""
520*9e94795aSAndroid Build Coastguard Workerbenchmarks:
521*9e94795aSAndroid Build Coastguard Worker{pretty.FormatTable(benchmark_table(self._benchmarks), prefix="  ")}
522*9e94795aSAndroid Build Coastguard Worker"""
523*9e94795aSAndroid Build Coastguard Worker
524*9e94795aSAndroid Build Coastguard Worker        parser = argparse.ArgumentParser(
525*9e94795aSAndroid Build Coastguard Worker                prog="benchmarks",
526*9e94795aSAndroid Build Coastguard Worker                allow_abbrev=False, # Don't let people write unsupportable scripts.
527*9e94795aSAndroid Build Coastguard Worker                formatter_class=argparse.RawDescriptionHelpFormatter,
528*9e94795aSAndroid Build Coastguard Worker                epilog=epilog,
529*9e94795aSAndroid Build Coastguard Worker                description="Run build system performance benchmarks.")
530*9e94795aSAndroid Build Coastguard Worker        self.parser = parser
531*9e94795aSAndroid Build Coastguard Worker
532*9e94795aSAndroid Build Coastguard Worker        parser.add_argument("--log-dir",
533*9e94795aSAndroid Build Coastguard Worker                            help="Directory for logs. Default is $TOP/../benchmarks/.")
534*9e94795aSAndroid Build Coastguard Worker        parser.add_argument("--dated-logs", action="store_true",
535*9e94795aSAndroid Build Coastguard Worker                            help="Append timestamp to log dir.")
536*9e94795aSAndroid Build Coastguard Worker        parser.add_argument("-n", action="store_true", dest="dry_run",
537*9e94795aSAndroid Build Coastguard Worker                            help="Dry run. Don't run the build commands but do everything else.")
538*9e94795aSAndroid Build Coastguard Worker        parser.add_argument("--tag",
539*9e94795aSAndroid Build Coastguard Worker                            help="Variant of the run, for when there are multiple perf runs.")
540*9e94795aSAndroid Build Coastguard Worker        parser.add_argument("--lunch", nargs="*",
541*9e94795aSAndroid Build Coastguard Worker                            help="Lunch combos to test")
542*9e94795aSAndroid Build Coastguard Worker        parser.add_argument("--iterations", type=int, default=1,
543*9e94795aSAndroid Build Coastguard Worker                            help="Number of iterations of each test to run.")
544*9e94795aSAndroid Build Coastguard Worker        parser.add_argument("--branch", type=str,
545*9e94795aSAndroid Build Coastguard Worker                            help="Specify branch. Otherwise a guess will be made based on repo.")
546*9e94795aSAndroid Build Coastguard Worker        parser.add_argument("--benchmark", nargs="*", default=[b.id for b in self._benchmarks],
547*9e94795aSAndroid Build Coastguard Worker                            metavar="BENCHMARKS",
548*9e94795aSAndroid Build Coastguard Worker                            help="Benchmarks to run.  Default suite will be run if omitted.")
549*9e94795aSAndroid Build Coastguard Worker        parser.add_argument("--dist-one", action="store_true",
550*9e94795aSAndroid Build Coastguard Worker                            help="Copy logs and metrics to the given dist dir. Requires that only"
551*9e94795aSAndroid Build Coastguard Worker                                + " one benchmark be supplied. Postroll steps will be skipped.")
552*9e94795aSAndroid Build Coastguard Worker
553*9e94795aSAndroid Build Coastguard Worker        self._args = parser.parse_args()
554*9e94795aSAndroid Build Coastguard Worker
555*9e94795aSAndroid Build Coastguard Worker        self._branch = self._branch()
556*9e94795aSAndroid Build Coastguard Worker        self._log_dir = self._log_dir()
557*9e94795aSAndroid Build Coastguard Worker        self._lunches = self._lunches()
558*9e94795aSAndroid Build Coastguard Worker
559*9e94795aSAndroid Build Coastguard Worker        # Validate the benchmark ids
560*9e94795aSAndroid Build Coastguard Worker        all_ids = [benchmark.id for benchmark in self._benchmarks]
561*9e94795aSAndroid Build Coastguard Worker        bad_ids = [id for id in self._args.benchmark if id not in all_ids]
562*9e94795aSAndroid Build Coastguard Worker        if bad_ids:
563*9e94795aSAndroid Build Coastguard Worker            for id in bad_ids:
564*9e94795aSAndroid Build Coastguard Worker                self._error(f"Invalid benchmark: {id}")
565*9e94795aSAndroid Build Coastguard Worker
566*9e94795aSAndroid Build Coastguard Worker        # --dist-one requires that only one benchmark be supplied
567*9e94795aSAndroid Build Coastguard Worker        if self._args.dist_one and len(self.Benchmarks()) != 1:
568*9e94795aSAndroid Build Coastguard Worker            self._error("--dist-one requires that exactly one --benchmark.")
569*9e94795aSAndroid Build Coastguard Worker
570*9e94795aSAndroid Build Coastguard Worker        if self._had_error:
571*9e94795aSAndroid Build Coastguard Worker            raise FatalError()
572*9e94795aSAndroid Build Coastguard Worker
573*9e94795aSAndroid Build Coastguard Worker    def Timestamp(self):
574*9e94795aSAndroid Build Coastguard Worker        return self._timestamp
575*9e94795aSAndroid Build Coastguard Worker
576*9e94795aSAndroid Build Coastguard Worker    def _branch(self):
577*9e94795aSAndroid Build Coastguard Worker        """Return the branch, either from the command line or by guessing from repo."""
578*9e94795aSAndroid Build Coastguard Worker        if self._args.branch:
579*9e94795aSAndroid Build Coastguard Worker            return self._args.branch
580*9e94795aSAndroid Build Coastguard Worker        try:
581*9e94795aSAndroid Build Coastguard Worker            branch = subprocess.check_output(f"cd {self.root}/.repo/manifests"
582*9e94795aSAndroid Build Coastguard Worker                        + " && git rev-parse --abbrev-ref --symbolic-full-name @{u}",
583*9e94795aSAndroid Build Coastguard Worker                    shell=True, encoding="utf-8")
584*9e94795aSAndroid Build Coastguard Worker            return branch.strip().split("/")[-1]
585*9e94795aSAndroid Build Coastguard Worker        except subprocess.CalledProcessError as ex:
586*9e94795aSAndroid Build Coastguard Worker            report_error("Can't get branch from .repo dir. Specify --branch argument")
587*9e94795aSAndroid Build Coastguard Worker            report_error(str(ex))
588*9e94795aSAndroid Build Coastguard Worker            raise FatalError()
589*9e94795aSAndroid Build Coastguard Worker
590*9e94795aSAndroid Build Coastguard Worker    def Branch(self):
591*9e94795aSAndroid Build Coastguard Worker        return self._branch
592*9e94795aSAndroid Build Coastguard Worker
593*9e94795aSAndroid Build Coastguard Worker    def _log_dir(self):
594*9e94795aSAndroid Build Coastguard Worker        "The log directory to use, based on the current options"
595*9e94795aSAndroid Build Coastguard Worker        if self._args.log_dir:
596*9e94795aSAndroid Build Coastguard Worker            d = pathlib.Path(self._args.log_dir).resolve().absolute()
597*9e94795aSAndroid Build Coastguard Worker        else:
598*9e94795aSAndroid Build Coastguard Worker            d = self.root.joinpath("..", utils.DEFAULT_REPORT_DIR)
599*9e94795aSAndroid Build Coastguard Worker        if self._args.dated_logs:
600*9e94795aSAndroid Build Coastguard Worker            d = d.joinpath(self._timestamp.strftime('%Y-%m-%d'))
601*9e94795aSAndroid Build Coastguard Worker        d = d.joinpath(self._branch)
602*9e94795aSAndroid Build Coastguard Worker        if self._args.tag:
603*9e94795aSAndroid Build Coastguard Worker            d = d.joinpath(self._args.tag)
604*9e94795aSAndroid Build Coastguard Worker        return d.resolve().absolute()
605*9e94795aSAndroid Build Coastguard Worker
606*9e94795aSAndroid Build Coastguard Worker    def LogDir(self):
607*9e94795aSAndroid Build Coastguard Worker        return self._log_dir
608*9e94795aSAndroid Build Coastguard Worker
609*9e94795aSAndroid Build Coastguard Worker    def Benchmarks(self):
610*9e94795aSAndroid Build Coastguard Worker        return [b for b in self._benchmarks if b.id in self._args.benchmark]
611*9e94795aSAndroid Build Coastguard Worker
612*9e94795aSAndroid Build Coastguard Worker    def Tag(self):
613*9e94795aSAndroid Build Coastguard Worker        return self._args.tag
614*9e94795aSAndroid Build Coastguard Worker
615*9e94795aSAndroid Build Coastguard Worker    def DryRun(self):
616*9e94795aSAndroid Build Coastguard Worker        return self._args.dry_run
617*9e94795aSAndroid Build Coastguard Worker
618*9e94795aSAndroid Build Coastguard Worker    def _lunches(self):
619*9e94795aSAndroid Build Coastguard Worker        def parse_lunch(lunch):
620*9e94795aSAndroid Build Coastguard Worker            parts = lunch.split("-")
621*9e94795aSAndroid Build Coastguard Worker            if len(parts) != 3:
622*9e94795aSAndroid Build Coastguard Worker                raise OptionsError(f"Invalid lunch combo: {lunch}")
623*9e94795aSAndroid Build Coastguard Worker            return Lunch(parts[0], parts[1], parts[2])
624*9e94795aSAndroid Build Coastguard Worker        # If they gave lunch targets on the command line use that
625*9e94795aSAndroid Build Coastguard Worker        if self._args.lunch:
626*9e94795aSAndroid Build Coastguard Worker            result = []
627*9e94795aSAndroid Build Coastguard Worker            # Split into Lunch objects
628*9e94795aSAndroid Build Coastguard Worker            for lunch in self._args.lunch:
629*9e94795aSAndroid Build Coastguard Worker                try:
630*9e94795aSAndroid Build Coastguard Worker                    result.append(parse_lunch(lunch))
631*9e94795aSAndroid Build Coastguard Worker                except OptionsError as ex:
632*9e94795aSAndroid Build Coastguard Worker                    self._error(ex.message)
633*9e94795aSAndroid Build Coastguard Worker            return result
634*9e94795aSAndroid Build Coastguard Worker        # Use whats in the environment
635*9e94795aSAndroid Build Coastguard Worker        product = os.getenv("TARGET_PRODUCT")
636*9e94795aSAndroid Build Coastguard Worker        release = os.getenv("TARGET_RELEASE")
637*9e94795aSAndroid Build Coastguard Worker        variant = os.getenv("TARGET_BUILD_VARIANT")
638*9e94795aSAndroid Build Coastguard Worker        if (not product) or (not release) or (not variant):
639*9e94795aSAndroid Build Coastguard Worker            # If they didn't give us anything, fail rather than guessing. There's no good
640*9e94795aSAndroid Build Coastguard Worker            # default for AOSP.
641*9e94795aSAndroid Build Coastguard Worker            self._error("No lunch combo specified. Either pass --lunch argument or run lunch.")
642*9e94795aSAndroid Build Coastguard Worker            return []
643*9e94795aSAndroid Build Coastguard Worker        return [Lunch(product, release, variant),]
644*9e94795aSAndroid Build Coastguard Worker
645*9e94795aSAndroid Build Coastguard Worker    def Lunches(self):
646*9e94795aSAndroid Build Coastguard Worker        return self._lunches
647*9e94795aSAndroid Build Coastguard Worker
648*9e94795aSAndroid Build Coastguard Worker    def Iterations(self):
649*9e94795aSAndroid Build Coastguard Worker        return self._args.iterations
650*9e94795aSAndroid Build Coastguard Worker
651*9e94795aSAndroid Build Coastguard Worker    def DistOne(self):
652*9e94795aSAndroid Build Coastguard Worker        return self._args.dist_one
653*9e94795aSAndroid Build Coastguard Worker
654*9e94795aSAndroid Build Coastguard Worker    def _init_benchmarks(self):
655*9e94795aSAndroid Build Coastguard Worker        """Initialize the list of benchmarks."""
656*9e94795aSAndroid Build Coastguard Worker        # Assumes that we've already chdired to the root of the tree.
657*9e94795aSAndroid Build Coastguard Worker        self._benchmarks = [
658*9e94795aSAndroid Build Coastguard Worker            Benchmark(
659*9e94795aSAndroid Build Coastguard Worker                      id="full_lunch",
660*9e94795aSAndroid Build Coastguard Worker                      title="Lunch from clean out",
661*9e94795aSAndroid Build Coastguard Worker                      change=Clean(),
662*9e94795aSAndroid Build Coastguard Worker                      dumpvars=True,
663*9e94795aSAndroid Build Coastguard Worker                      preroll=0,
664*9e94795aSAndroid Build Coastguard Worker                      postroll=0,
665*9e94795aSAndroid Build Coastguard Worker            ),
666*9e94795aSAndroid Build Coastguard Worker            Benchmark(
667*9e94795aSAndroid Build Coastguard Worker                      id="noop_lunch",
668*9e94795aSAndroid Build Coastguard Worker                      title="Lunch with no change",
669*9e94795aSAndroid Build Coastguard Worker                      change=NoChange(),
670*9e94795aSAndroid Build Coastguard Worker                      dumpvars=True,
671*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
672*9e94795aSAndroid Build Coastguard Worker                      postroll=0,
673*9e94795aSAndroid Build Coastguard Worker            ),
674*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="full",
675*9e94795aSAndroid Build Coastguard Worker                      title="Full build",
676*9e94795aSAndroid Build Coastguard Worker                      change=Clean(),
677*9e94795aSAndroid Build Coastguard Worker                      modules=["droid"],
678*9e94795aSAndroid Build Coastguard Worker                      preroll=0,
679*9e94795aSAndroid Build Coastguard Worker                      postroll=3,
680*9e94795aSAndroid Build Coastguard Worker                      ),
681*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="nochange",
682*9e94795aSAndroid Build Coastguard Worker                      title="No change",
683*9e94795aSAndroid Build Coastguard Worker                      change=NoChange(),
684*9e94795aSAndroid Build Coastguard Worker                      modules=["droid"],
685*9e94795aSAndroid Build Coastguard Worker                      preroll=2,
686*9e94795aSAndroid Build Coastguard Worker                      postroll=3,
687*9e94795aSAndroid Build Coastguard Worker                      ),
688*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="unreferenced",
689*9e94795aSAndroid Build Coastguard Worker                      title="Create unreferenced file",
690*9e94795aSAndroid Build Coastguard Worker                      change=Create("bionic/unreferenced.txt"),
691*9e94795aSAndroid Build Coastguard Worker                      modules=["droid"],
692*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
693*9e94795aSAndroid Build Coastguard Worker                      postroll=2,
694*9e94795aSAndroid Build Coastguard Worker                      ),
695*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="modify_bp",
696*9e94795aSAndroid Build Coastguard Worker                      title="Modify Android.bp",
697*9e94795aSAndroid Build Coastguard Worker                      change=Modify("bionic/libc/Android.bp", Comment("//")),
698*9e94795aSAndroid Build Coastguard Worker                      modules=["droid"],
699*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
700*9e94795aSAndroid Build Coastguard Worker                      postroll=3,
701*9e94795aSAndroid Build Coastguard Worker                      ),
702*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="modify_stdio",
703*9e94795aSAndroid Build Coastguard Worker                      title="Modify stdio.cpp",
704*9e94795aSAndroid Build Coastguard Worker                      change=Modify("bionic/libc/stdio/stdio.cpp", Comment("//")),
705*9e94795aSAndroid Build Coastguard Worker                      modules=["libc"],
706*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
707*9e94795aSAndroid Build Coastguard Worker                      postroll=2,
708*9e94795aSAndroid Build Coastguard Worker                      ),
709*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="modify_adbd",
710*9e94795aSAndroid Build Coastguard Worker                      title="Modify adbd",
711*9e94795aSAndroid Build Coastguard Worker                      change=Modify("packages/modules/adb/daemon/main.cpp", Comment("//")),
712*9e94795aSAndroid Build Coastguard Worker                      modules=["adbd"],
713*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
714*9e94795aSAndroid Build Coastguard Worker                      postroll=2,
715*9e94795aSAndroid Build Coastguard Worker                      ),
716*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="services_private_field",
717*9e94795aSAndroid Build Coastguard Worker                      title="Add private field to ActivityManagerService.java",
718*9e94795aSAndroid Build Coastguard Worker                      change=AddJavaField("frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java",
719*9e94795aSAndroid Build Coastguard Worker                                          "private"),
720*9e94795aSAndroid Build Coastguard Worker                      modules=["services"],
721*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
722*9e94795aSAndroid Build Coastguard Worker                      postroll=2,
723*9e94795aSAndroid Build Coastguard Worker                      ),
724*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="services_public_field",
725*9e94795aSAndroid Build Coastguard Worker                      title="Add public field to ActivityManagerService.java",
726*9e94795aSAndroid Build Coastguard Worker                      change=AddJavaField("frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java",
727*9e94795aSAndroid Build Coastguard Worker                                          "/** @hide */ public"),
728*9e94795aSAndroid Build Coastguard Worker                      modules=["services"],
729*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
730*9e94795aSAndroid Build Coastguard Worker                      postroll=2,
731*9e94795aSAndroid Build Coastguard Worker                      ),
732*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="services_api",
733*9e94795aSAndroid Build Coastguard Worker                      title="Add API to ActivityManagerService.javaa",
734*9e94795aSAndroid Build Coastguard Worker                      change=AddJavaField("frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java",
735*9e94795aSAndroid Build Coastguard Worker                                          "@android.annotation.SuppressLint(\"UnflaggedApi\") public"),
736*9e94795aSAndroid Build Coastguard Worker                      modules=["services"],
737*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
738*9e94795aSAndroid Build Coastguard Worker                      postroll=2,
739*9e94795aSAndroid Build Coastguard Worker                      ),
740*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="framework_private_field",
741*9e94795aSAndroid Build Coastguard Worker                      title="Add private field to Settings.java",
742*9e94795aSAndroid Build Coastguard Worker                      change=AddJavaField("frameworks/base/core/java/android/provider/Settings.java",
743*9e94795aSAndroid Build Coastguard Worker                                          "private"),
744*9e94795aSAndroid Build Coastguard Worker                      modules=["framework-minus-apex"],
745*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
746*9e94795aSAndroid Build Coastguard Worker                      postroll=2,
747*9e94795aSAndroid Build Coastguard Worker                      ),
748*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="framework_public_field",
749*9e94795aSAndroid Build Coastguard Worker                      title="Add public field to Settings.java",
750*9e94795aSAndroid Build Coastguard Worker                      change=AddJavaField("frameworks/base/core/java/android/provider/Settings.java",
751*9e94795aSAndroid Build Coastguard Worker                                          "/** @hide */ public"),
752*9e94795aSAndroid Build Coastguard Worker                      modules=["framework-minus-apex"],
753*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
754*9e94795aSAndroid Build Coastguard Worker                      postroll=2,
755*9e94795aSAndroid Build Coastguard Worker                      ),
756*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="framework_api",
757*9e94795aSAndroid Build Coastguard Worker                      title="Add API to Settings.java",
758*9e94795aSAndroid Build Coastguard Worker                      change=ChangePublicApi(),
759*9e94795aSAndroid Build Coastguard Worker                      modules=["api-stubs-docs-non-updatable-update-current-api", "framework-minus-apex"],
760*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
761*9e94795aSAndroid Build Coastguard Worker                      postroll=2,
762*9e94795aSAndroid Build Coastguard Worker                      ),
763*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="modify_framework_resource",
764*9e94795aSAndroid Build Coastguard Worker                      title="Modify framework resource",
765*9e94795aSAndroid Build Coastguard Worker                      change=Modify("frameworks/base/core/res/res/values/config.xml",
766*9e94795aSAndroid Build Coastguard Worker                                    lambda: str(uuid.uuid4()),
767*9e94795aSAndroid Build Coastguard Worker                                    before="</string>"),
768*9e94795aSAndroid Build Coastguard Worker                      modules=["framework-minus-apex"],
769*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
770*9e94795aSAndroid Build Coastguard Worker                      postroll=2,
771*9e94795aSAndroid Build Coastguard Worker                      ),
772*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="add_framework_resource",
773*9e94795aSAndroid Build Coastguard Worker                      title="Add framework resource",
774*9e94795aSAndroid Build Coastguard Worker                      change=Modify("frameworks/base/core/res/res/values/config.xml",
775*9e94795aSAndroid Build Coastguard Worker                                    lambda: f"<string name=\"BENCHMARK\">{uuid.uuid4()}</string>",
776*9e94795aSAndroid Build Coastguard Worker                                    before="</resources>"),
777*9e94795aSAndroid Build Coastguard Worker                      modules=["framework-minus-apex"],
778*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
779*9e94795aSAndroid Build Coastguard Worker                      postroll=2,
780*9e94795aSAndroid Build Coastguard Worker                      ),
781*9e94795aSAndroid Build Coastguard Worker            Benchmark(id="add_systemui_field",
782*9e94795aSAndroid Build Coastguard Worker                      title="Add SystemUI field",
783*9e94795aSAndroid Build Coastguard Worker                      change=AddJavaField("frameworks/base/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java",
784*9e94795aSAndroid Build Coastguard Worker                                    "public"),
785*9e94795aSAndroid Build Coastguard Worker                      modules=["SystemUI"],
786*9e94795aSAndroid Build Coastguard Worker                      preroll=1,
787*9e94795aSAndroid Build Coastguard Worker                      postroll=2,
788*9e94795aSAndroid Build Coastguard Worker                      ),
789*9e94795aSAndroid Build Coastguard Worker        ]
790*9e94795aSAndroid Build Coastguard Worker
791*9e94795aSAndroid Build Coastguard Worker    def _error(self, message):
792*9e94795aSAndroid Build Coastguard Worker        report_error(message)
793*9e94795aSAndroid Build Coastguard Worker        self._had_error = True
794*9e94795aSAndroid Build Coastguard Worker
795*9e94795aSAndroid Build Coastguard Worker
796*9e94795aSAndroid Build Coastguard Workerdef report_error(message):
797*9e94795aSAndroid Build Coastguard Worker    sys.stderr.write(f"error: {message}\n")
798*9e94795aSAndroid Build Coastguard Worker
799*9e94795aSAndroid Build Coastguard Worker
800*9e94795aSAndroid Build Coastguard Workerdef main(argv):
801*9e94795aSAndroid Build Coastguard Worker    try:
802*9e94795aSAndroid Build Coastguard Worker        options = Options()
803*9e94795aSAndroid Build Coastguard Worker        runner = Runner(options)
804*9e94795aSAndroid Build Coastguard Worker        runner.Run()
805*9e94795aSAndroid Build Coastguard Worker    except FatalError:
806*9e94795aSAndroid Build Coastguard Worker        sys.stderr.write(f"FAILED\n")
807*9e94795aSAndroid Build Coastguard Worker        sys.exit(1)
808*9e94795aSAndroid Build Coastguard Worker
809*9e94795aSAndroid Build Coastguard Worker
810*9e94795aSAndroid Build Coastguard Workerif __name__ == "__main__":
811*9e94795aSAndroid Build Coastguard Worker    main(sys.argv)
812