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