xref: /aosp_15_r20/build/bazel/scripts/print_analysis_metrics.py (revision 7594170e27e0732bc44b93d1440d87a54b6ffe7c)
1*7594170eSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*7594170eSAndroid Build Coastguard Worker#
3*7594170eSAndroid Build Coastguard Worker# Copyright (C) 2022 The Android Open Source Project
4*7594170eSAndroid Build Coastguard Worker#
5*7594170eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*7594170eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*7594170eSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*7594170eSAndroid Build Coastguard Worker#
9*7594170eSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*7594170eSAndroid Build Coastguard Worker#
11*7594170eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*7594170eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*7594170eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*7594170eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*7594170eSAndroid Build Coastguard Worker# limitations under the License.
16*7594170eSAndroid Build Coastguard Worker"""A tool to print human-readable metrics information regarding the last build.
17*7594170eSAndroid Build Coastguard Worker
18*7594170eSAndroid Build Coastguard WorkerBy default, the consumed files will be located in $ANDROID_BUILD_TOP/out/. You
19*7594170eSAndroid Build Coastguard Workermay pass in a different directory instead using the metrics_files_dir flag.
20*7594170eSAndroid Build Coastguard Worker"""
21*7594170eSAndroid Build Coastguard Worker
22*7594170eSAndroid Build Coastguard Workerimport argparse
23*7594170eSAndroid Build Coastguard Workerimport json
24*7594170eSAndroid Build Coastguard Workerimport os
25*7594170eSAndroid Build Coastguard Workerimport shutil
26*7594170eSAndroid Build Coastguard Workerimport subprocess
27*7594170eSAndroid Build Coastguard Workerimport sys
28*7594170eSAndroid Build Coastguard Workerimport tarfile
29*7594170eSAndroid Build Coastguard Worker
30*7594170eSAndroid Build Coastguard Workerfrom bazel_metrics_proto.bazel_metrics_pb2 import BazelMetrics
31*7594170eSAndroid Build Coastguard Workerfrom bp2build_metrics_proto.bp2build_metrics_pb2 import Bp2BuildMetrics, UnconvertedReasonType
32*7594170eSAndroid Build Coastguard Workerfrom google.protobuf import json_format
33*7594170eSAndroid Build Coastguard Workerfrom metrics_proto.metrics_pb2 import MetricsBase, SoongBuildMetrics
34*7594170eSAndroid Build Coastguard Worker
35*7594170eSAndroid Build Coastguard Worker
36*7594170eSAndroid Build Coastguard Workerclass Event(object):
37*7594170eSAndroid Build Coastguard Worker  """Contains nested event data.
38*7594170eSAndroid Build Coastguard Worker
39*7594170eSAndroid Build Coastguard Worker  Fields:
40*7594170eSAndroid Build Coastguard Worker    name: The short name of this event e.g. the 'b' in an event called a.b.
41*7594170eSAndroid Build Coastguard Worker    start_time_relative_ns: Time since the epoch that the event started
42*7594170eSAndroid Build Coastguard Worker    duration_ns: Duration of this event, including time spent in children.
43*7594170eSAndroid Build Coastguard Worker  """
44*7594170eSAndroid Build Coastguard Worker
45*7594170eSAndroid Build Coastguard Worker  def __init__(self, name, start_time_relative_ns, duration_ns):
46*7594170eSAndroid Build Coastguard Worker    self.name = name
47*7594170eSAndroid Build Coastguard Worker    self.start_time_relative_ns = start_time_relative_ns
48*7594170eSAndroid Build Coastguard Worker    self.duration_ns = duration_ns
49*7594170eSAndroid Build Coastguard Worker
50*7594170eSAndroid Build Coastguard Worker
51*7594170eSAndroid Build Coastguard Workerdef _get_output_file(output_dir, filename):
52*7594170eSAndroid Build Coastguard Worker  file_base = os.path.splitext(filename)[0]
53*7594170eSAndroid Build Coastguard Worker  return os.path.join(output_dir, file_base + ".json")
54*7594170eSAndroid Build Coastguard Worker
55*7594170eSAndroid Build Coastguard Worker
56*7594170eSAndroid Build Coastguard Workerdef _get_default_out_dir(metrics_dir):
57*7594170eSAndroid Build Coastguard Worker  return os.path.join(metrics_dir, "analyze_build_output")
58*7594170eSAndroid Build Coastguard Worker
59*7594170eSAndroid Build Coastguard Worker
60*7594170eSAndroid Build Coastguard Workerdef _get_default_metrics_dir():
61*7594170eSAndroid Build Coastguard Worker  """Returns the filepath for the build output."""
62*7594170eSAndroid Build Coastguard Worker  out_dir = os.getenv("OUT_DIR")
63*7594170eSAndroid Build Coastguard Worker  if out_dir:
64*7594170eSAndroid Build Coastguard Worker    return out_dir
65*7594170eSAndroid Build Coastguard Worker  build_top = os.getenv("ANDROID_BUILD_TOP")
66*7594170eSAndroid Build Coastguard Worker  if not build_top:
67*7594170eSAndroid Build Coastguard Worker    raise Exception(
68*7594170eSAndroid Build Coastguard Worker        "$ANDROID_BUILD_TOP not found in environment. Have you run lunch?"
69*7594170eSAndroid Build Coastguard Worker    )
70*7594170eSAndroid Build Coastguard Worker  return os.path.join(build_top, "out")
71*7594170eSAndroid Build Coastguard Worker
72*7594170eSAndroid Build Coastguard Worker
73*7594170eSAndroid Build Coastguard Workerdef _write_event(out, event):
74*7594170eSAndroid Build Coastguard Worker  """Writes an event. See _write_events for args."""
75*7594170eSAndroid Build Coastguard Worker  out.write(
76*7594170eSAndroid Build Coastguard Worker      "%(start)9s  %(duration)9s  %(name)s\n"
77*7594170eSAndroid Build Coastguard Worker      % {
78*7594170eSAndroid Build Coastguard Worker          "start": _format_ns(event.start_time_relative_ns),
79*7594170eSAndroid Build Coastguard Worker          "duration": _format_ns(event.duration_ns),
80*7594170eSAndroid Build Coastguard Worker          "name": event.name,
81*7594170eSAndroid Build Coastguard Worker      }
82*7594170eSAndroid Build Coastguard Worker  )
83*7594170eSAndroid Build Coastguard Worker
84*7594170eSAndroid Build Coastguard Worker
85*7594170eSAndroid Build Coastguard Workerdef _print_metrics_event_times(description, metrics):
86*7594170eSAndroid Build Coastguard Worker  # Bail if there are no events
87*7594170eSAndroid Build Coastguard Worker  raw_events = metrics.events
88*7594170eSAndroid Build Coastguard Worker  if not raw_events:
89*7594170eSAndroid Build Coastguard Worker    print("%s: No events to display" % description)
90*7594170eSAndroid Build Coastguard Worker    return
91*7594170eSAndroid Build Coastguard Worker  print("-- %s events --" % description)
92*7594170eSAndroid Build Coastguard Worker
93*7594170eSAndroid Build Coastguard Worker  # Update the start times to be based on the first event
94*7594170eSAndroid Build Coastguard Worker  first_time_ns = min([event.start_time for event in raw_events])
95*7594170eSAndroid Build Coastguard Worker  events = [
96*7594170eSAndroid Build Coastguard Worker      Event(
97*7594170eSAndroid Build Coastguard Worker          getattr(e, "description", e.name),
98*7594170eSAndroid Build Coastguard Worker          e.start_time - first_time_ns,
99*7594170eSAndroid Build Coastguard Worker          e.real_time,
100*7594170eSAndroid Build Coastguard Worker      )
101*7594170eSAndroid Build Coastguard Worker      for e in raw_events
102*7594170eSAndroid Build Coastguard Worker  ]
103*7594170eSAndroid Build Coastguard Worker
104*7594170eSAndroid Build Coastguard Worker  # Sort by start time so the nesting also is sorted by time
105*7594170eSAndroid Build Coastguard Worker  events.sort(key=lambda x: x.start_time_relative_ns)
106*7594170eSAndroid Build Coastguard Worker
107*7594170eSAndroid Build Coastguard Worker  # Output the results
108*7594170eSAndroid Build Coastguard Worker  print("    start   duration")
109*7594170eSAndroid Build Coastguard Worker
110*7594170eSAndroid Build Coastguard Worker  for event in events:
111*7594170eSAndroid Build Coastguard Worker    _write_event(sys.stdout, event)
112*7594170eSAndroid Build Coastguard Worker  print()
113*7594170eSAndroid Build Coastguard Worker
114*7594170eSAndroid Build Coastguard Worker
115*7594170eSAndroid Build Coastguard Workerdef _format_ns(duration_ns):
116*7594170eSAndroid Build Coastguard Worker  "Pretty print duration in nanoseconds"
117*7594170eSAndroid Build Coastguard Worker  return "%.02fs" % (duration_ns / 1_000_000_000)
118*7594170eSAndroid Build Coastguard Worker
119*7594170eSAndroid Build Coastguard Worker
120*7594170eSAndroid Build Coastguard Workerdef _read_data(filepath, proto):
121*7594170eSAndroid Build Coastguard Worker  with open(filepath, "rb") as f:
122*7594170eSAndroid Build Coastguard Worker    proto.ParseFromString(f.read())
123*7594170eSAndroid Build Coastguard Worker    f.close()
124*7594170eSAndroid Build Coastguard Worker
125*7594170eSAndroid Build Coastguard Worker
126*7594170eSAndroid Build Coastguard Workerdef _maybe_save_data(proto, filename, args):
127*7594170eSAndroid Build Coastguard Worker  if args.skip_metrics:
128*7594170eSAndroid Build Coastguard Worker    return
129*7594170eSAndroid Build Coastguard Worker  json_out = json_format.MessageToJson(proto)
130*7594170eSAndroid Build Coastguard Worker  output_filepath = _get_output_file(args.output_dir, filename)
131*7594170eSAndroid Build Coastguard Worker  _save_file(json_out, output_filepath)
132*7594170eSAndroid Build Coastguard Worker
133*7594170eSAndroid Build Coastguard Worker
134*7594170eSAndroid Build Coastguard Workerdef _save_file(data, file):
135*7594170eSAndroid Build Coastguard Worker  with open(file, "w") as f:
136*7594170eSAndroid Build Coastguard Worker    f.write(data)
137*7594170eSAndroid Build Coastguard Worker    f.close()
138*7594170eSAndroid Build Coastguard Worker
139*7594170eSAndroid Build Coastguard Worker
140*7594170eSAndroid Build Coastguard Workerdef _handle_missing_metrics(args, filename):
141*7594170eSAndroid Build Coastguard Worker  """Handles cleanup for a metrics file that doesn't exist.
142*7594170eSAndroid Build Coastguard Worker
143*7594170eSAndroid Build Coastguard Worker  This will delete any output files under the tool's output directory that
144*7594170eSAndroid Build Coastguard Worker  would have been generated as a result of a metrics file from a previous
145*7594170eSAndroid Build Coastguard Worker  build. This prevents stale analysis files from polluting the output dir.
146*7594170eSAndroid Build Coastguard Worker  """
147*7594170eSAndroid Build Coastguard Worker  if args.skip_metrics:
148*7594170eSAndroid Build Coastguard Worker    # If skip_metrics is enabled, then don't write or delete any data.
149*7594170eSAndroid Build Coastguard Worker    return
150*7594170eSAndroid Build Coastguard Worker  output_filepath = _get_output_file(args.output_dir, filename)
151*7594170eSAndroid Build Coastguard Worker  if os.path.exists(output_filepath):
152*7594170eSAndroid Build Coastguard Worker    os.remove(output_filepath)
153*7594170eSAndroid Build Coastguard Worker
154*7594170eSAndroid Build Coastguard Worker
155*7594170eSAndroid Build Coastguard Workerdef process_timing_mode(args):
156*7594170eSAndroid Build Coastguard Worker  metrics_files_dir = args.metrics_files_dir
157*7594170eSAndroid Build Coastguard Worker  if not args.skip_metrics:
158*7594170eSAndroid Build Coastguard Worker    os.makedirs(args.output_dir, exist_ok=True)
159*7594170eSAndroid Build Coastguard Worker    print("Writing build analysis files to " + args.output_dir, file=sys.stderr)
160*7594170eSAndroid Build Coastguard Worker
161*7594170eSAndroid Build Coastguard Worker  bp2build_file = os.path.join(metrics_files_dir, "bp2build_metrics.pb")
162*7594170eSAndroid Build Coastguard Worker  if os.path.exists(bp2build_file):
163*7594170eSAndroid Build Coastguard Worker    bp2build_metrics = Bp2BuildMetrics()
164*7594170eSAndroid Build Coastguard Worker    _read_data(bp2build_file, bp2build_metrics)
165*7594170eSAndroid Build Coastguard Worker    _print_metrics_event_times("bp2build", bp2build_metrics)
166*7594170eSAndroid Build Coastguard Worker    _maybe_save_data(bp2build_metrics, "bp2build_metrics.pb", args)
167*7594170eSAndroid Build Coastguard Worker  else:
168*7594170eSAndroid Build Coastguard Worker    _handle_missing_metrics(args, "bp2build_metrics.pb")
169*7594170eSAndroid Build Coastguard Worker
170*7594170eSAndroid Build Coastguard Worker  soong_build_file = os.path.join(metrics_files_dir, "soong_build_metrics.pb")
171*7594170eSAndroid Build Coastguard Worker  if os.path.exists(soong_build_file):
172*7594170eSAndroid Build Coastguard Worker    soong_build_metrics = SoongBuildMetrics()
173*7594170eSAndroid Build Coastguard Worker    _read_data(soong_build_file, soong_build_metrics)
174*7594170eSAndroid Build Coastguard Worker    _print_metrics_event_times("soong_build", soong_build_metrics)
175*7594170eSAndroid Build Coastguard Worker    _maybe_save_data(soong_build_metrics, "soong_build_metrics.pb", args)
176*7594170eSAndroid Build Coastguard Worker  else:
177*7594170eSAndroid Build Coastguard Worker    _handle_missing_metrics(args, "soong_build_metrics.pb")
178*7594170eSAndroid Build Coastguard Worker
179*7594170eSAndroid Build Coastguard Worker  soong_metrics_file = os.path.join(metrics_files_dir, "soong_metrics")
180*7594170eSAndroid Build Coastguard Worker  if os.path.exists(soong_metrics_file):
181*7594170eSAndroid Build Coastguard Worker    metrics_base = MetricsBase()
182*7594170eSAndroid Build Coastguard Worker    _read_data(soong_metrics_file, metrics_base)
183*7594170eSAndroid Build Coastguard Worker    _maybe_save_data(metrics_base, "soong_metrics", args)
184*7594170eSAndroid Build Coastguard Worker  else:
185*7594170eSAndroid Build Coastguard Worker    _handle_missing_metrics(args, "soong_metrics")
186*7594170eSAndroid Build Coastguard Worker
187*7594170eSAndroid Build Coastguard Worker  bazel_metrics_file = os.path.join(metrics_files_dir, "bazel_metrics.pb")
188*7594170eSAndroid Build Coastguard Worker  if os.path.exists(bazel_metrics_file):
189*7594170eSAndroid Build Coastguard Worker    bazel_metrics = BazelMetrics()
190*7594170eSAndroid Build Coastguard Worker    _read_data(bazel_metrics_file, bazel_metrics)
191*7594170eSAndroid Build Coastguard Worker    _maybe_save_data(bazel_metrics, "bazel_metrics.pb", args)
192*7594170eSAndroid Build Coastguard Worker  else:
193*7594170eSAndroid Build Coastguard Worker    _handle_missing_metrics(args, "bazel_metrics.pb")
194*7594170eSAndroid Build Coastguard Worker
195*7594170eSAndroid Build Coastguard Worker
196*7594170eSAndroid Build Coastguard Workerdef process_build_files_mode(args):
197*7594170eSAndroid Build Coastguard Worker  if args.skip_metrics:
198*7594170eSAndroid Build Coastguard Worker    raise Exception("build_files mode incompatible with --skip-metrics")
199*7594170eSAndroid Build Coastguard Worker  os.makedirs(args.output_dir, exist_ok=True)
200*7594170eSAndroid Build Coastguard Worker  tar_out = os.path.join(args.output_dir, "build_files.tar.gz")
201*7594170eSAndroid Build Coastguard Worker
202*7594170eSAndroid Build Coastguard Worker  os.chdir(args.metrics_files_dir)
203*7594170eSAndroid Build Coastguard Worker
204*7594170eSAndroid Build Coastguard Worker  if os.path.exists(tar_out):
205*7594170eSAndroid Build Coastguard Worker    os.remove(tar_out)
206*7594170eSAndroid Build Coastguard Worker  print("adding build files to", tar_out, "...", file=sys.stderr)
207*7594170eSAndroid Build Coastguard Worker
208*7594170eSAndroid Build Coastguard Worker  with tarfile.open(tar_out, "w:gz", dereference=True) as tar:
209*7594170eSAndroid Build Coastguard Worker    for root, dirs, files in os.walk("."):
210*7594170eSAndroid Build Coastguard Worker      for file in files:
211*7594170eSAndroid Build Coastguard Worker        if (
212*7594170eSAndroid Build Coastguard Worker            file.endswith(".bzl")
213*7594170eSAndroid Build Coastguard Worker            or file.endswith("BUILD")
214*7594170eSAndroid Build Coastguard Worker            or file.endswith("BUILD.bazel")
215*7594170eSAndroid Build Coastguard Worker        ):
216*7594170eSAndroid Build Coastguard Worker          tar.add(os.path.join(root, file), arcname=os.path.join(root, file))
217*7594170eSAndroid Build Coastguard Worker
218*7594170eSAndroid Build Coastguard Worker
219*7594170eSAndroid Build Coastguard Workerdef process_bp2build_mode(args):
220*7594170eSAndroid Build Coastguard Worker  metrics_files_dir = args.metrics_files_dir
221*7594170eSAndroid Build Coastguard Worker  if not args.skip_metrics:
222*7594170eSAndroid Build Coastguard Worker    os.makedirs(args.output_dir, exist_ok=True)
223*7594170eSAndroid Build Coastguard Worker    print("Writing build analysis files to " + args.output_dir, file=sys.stderr)
224*7594170eSAndroid Build Coastguard Worker
225*7594170eSAndroid Build Coastguard Worker  bp2build_file = os.path.join(metrics_files_dir, "bp2build_metrics.pb")
226*7594170eSAndroid Build Coastguard Worker  if not os.path.exists(bp2build_file):
227*7594170eSAndroid Build Coastguard Worker    raise Exception("bp2build mode requires that the last build ran bp2build")
228*7594170eSAndroid Build Coastguard Worker
229*7594170eSAndroid Build Coastguard Worker  bp2build_metrics = Bp2BuildMetrics()
230*7594170eSAndroid Build Coastguard Worker  _read_data(bp2build_file, bp2build_metrics)
231*7594170eSAndroid Build Coastguard Worker  _maybe_save_data(bp2build_metrics, "bp2build_metrics.pb", args)
232*7594170eSAndroid Build Coastguard Worker  converted_modules = {}
233*7594170eSAndroid Build Coastguard Worker  for module in bp2build_metrics.convertedModules:
234*7594170eSAndroid Build Coastguard Worker    converted_modules[module] = True
235*7594170eSAndroid Build Coastguard Worker
236*7594170eSAndroid Build Coastguard Worker  if len(args.module_names) > 0:
237*7594170eSAndroid Build Coastguard Worker    modules_to_report = args.module_names
238*7594170eSAndroid Build Coastguard Worker  else:
239*7594170eSAndroid Build Coastguard Worker    all_modules = {}
240*7594170eSAndroid Build Coastguard Worker    for m in converted_modules:
241*7594170eSAndroid Build Coastguard Worker      all_modules[m] = True
242*7594170eSAndroid Build Coastguard Worker    for m in bp2build_metrics.unconvertedModules:
243*7594170eSAndroid Build Coastguard Worker      all_modules[m] = True
244*7594170eSAndroid Build Coastguard Worker    modules_to_report = sorted(all_modules)
245*7594170eSAndroid Build Coastguard Worker
246*7594170eSAndroid Build Coastguard Worker  for name in modules_to_report:
247*7594170eSAndroid Build Coastguard Worker    if name in converted_modules:
248*7594170eSAndroid Build Coastguard Worker      print(name, "converted successfully.")
249*7594170eSAndroid Build Coastguard Worker    elif name in bp2build_metrics.unconvertedModules:
250*7594170eSAndroid Build Coastguard Worker      unconverted_summary = name + " not converted: "
251*7594170eSAndroid Build Coastguard Worker      t = bp2build_metrics.unconvertedModules[name].type
252*7594170eSAndroid Build Coastguard Worker      if t > -1 and t < len(UnconvertedReasonType.keys()):
253*7594170eSAndroid Build Coastguard Worker        unconverted_summary += UnconvertedReasonType.keys()[t]
254*7594170eSAndroid Build Coastguard Worker      else:
255*7594170eSAndroid Build Coastguard Worker        unconverted_summary += "UNKNOWN_TYPE"
256*7594170eSAndroid Build Coastguard Worker      if len(bp2build_metrics.unconvertedModules[name].detail) > 0:
257*7594170eSAndroid Build Coastguard Worker        unconverted_summary += (
258*7594170eSAndroid Build Coastguard Worker            " detail: " + bp2build_metrics.unconvertedModules[name].detail
259*7594170eSAndroid Build Coastguard Worker        )
260*7594170eSAndroid Build Coastguard Worker      print(unconverted_summary)
261*7594170eSAndroid Build Coastguard Worker    else:
262*7594170eSAndroid Build Coastguard Worker      print(name, "does not exist.")
263*7594170eSAndroid Build Coastguard Worker
264*7594170eSAndroid Build Coastguard Worker
265*7594170eSAndroid Build Coastguard Workerdef _define_global_flags(parser, suppress_default=False):
266*7594170eSAndroid Build Coastguard Worker  """Adds global flags to the given parser object.
267*7594170eSAndroid Build Coastguard Worker
268*7594170eSAndroid Build Coastguard Worker  Global flags should be added to both the global args parser and subcommand
269*7594170eSAndroid Build Coastguard Worker  parsers. This allows global flags to be specified before or after the
270*7594170eSAndroid Build Coastguard Worker  subcommand.
271*7594170eSAndroid Build Coastguard Worker
272*7594170eSAndroid Build Coastguard Worker  Subcommand parser binding should pass suppress_default=True. This uses the
273*7594170eSAndroid Build Coastguard Worker  default value specified in the global parser.
274*7594170eSAndroid Build Coastguard Worker  """
275*7594170eSAndroid Build Coastguard Worker  parser.add_argument(
276*7594170eSAndroid Build Coastguard Worker      "--metrics_files_dir",
277*7594170eSAndroid Build Coastguard Worker      default=(
278*7594170eSAndroid Build Coastguard Worker          argparse.SUPPRESS if suppress_default else _get_default_metrics_dir()
279*7594170eSAndroid Build Coastguard Worker      ),
280*7594170eSAndroid Build Coastguard Worker      help="The directory contained metrics files to analyze."
281*7594170eSAndroid Build Coastguard Worker      + " Defaults to $OUT_DIR if set, $ANDROID_BUILD_TOP/out otherwise.",
282*7594170eSAndroid Build Coastguard Worker  )
283*7594170eSAndroid Build Coastguard Worker  parser.add_argument(
284*7594170eSAndroid Build Coastguard Worker      "--skip-metrics",
285*7594170eSAndroid Build Coastguard Worker      action="store_true",
286*7594170eSAndroid Build Coastguard Worker      default=(argparse.SUPPRESS if suppress_default else None),
287*7594170eSAndroid Build Coastguard Worker      help="If set, do not save the output of printproto commands.",
288*7594170eSAndroid Build Coastguard Worker  )
289*7594170eSAndroid Build Coastguard Worker  parser.add_argument(
290*7594170eSAndroid Build Coastguard Worker      "--output_dir",
291*7594170eSAndroid Build Coastguard Worker      default=(argparse.SUPPRESS if suppress_default else None),
292*7594170eSAndroid Build Coastguard Worker      help="The directory to save analyzed proto output to. "
293*7594170eSAndroid Build Coastguard Worker      + "If unspecified, will default to the directory specified with"
294*7594170eSAndroid Build Coastguard Worker      " --metrics_files_dir + '/analyze_build_output/'",
295*7594170eSAndroid Build Coastguard Worker  )
296*7594170eSAndroid Build Coastguard Worker
297*7594170eSAndroid Build Coastguard Worker
298*7594170eSAndroid Build Coastguard Workerdef main():
299*7594170eSAndroid Build Coastguard Worker  # Parse args
300*7594170eSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(
301*7594170eSAndroid Build Coastguard Worker      description=(
302*7594170eSAndroid Build Coastguard Worker          "Analyzes build artifacts from the user's most recent build. Prints"
303*7594170eSAndroid Build Coastguard Worker          " and/or saves data in a user-friendly format. See"
304*7594170eSAndroid Build Coastguard Worker          " subcommand-specific help for analysis options."
305*7594170eSAndroid Build Coastguard Worker      ),
306*7594170eSAndroid Build Coastguard Worker      prog="analyze_build",
307*7594170eSAndroid Build Coastguard Worker  )
308*7594170eSAndroid Build Coastguard Worker  _define_global_flags(parser)
309*7594170eSAndroid Build Coastguard Worker  subparsers = parser.add_subparsers(
310*7594170eSAndroid Build Coastguard Worker      title="subcommands",
311*7594170eSAndroid Build Coastguard Worker      help='types of analysis to run, "timing" by default.',
312*7594170eSAndroid Build Coastguard Worker      dest="mode",
313*7594170eSAndroid Build Coastguard Worker  )
314*7594170eSAndroid Build Coastguard Worker  timing_parser = subparsers.add_parser(
315*7594170eSAndroid Build Coastguard Worker      "timing", help="print per-phase build timing information"
316*7594170eSAndroid Build Coastguard Worker  )
317*7594170eSAndroid Build Coastguard Worker  _define_global_flags(timing_parser, True)
318*7594170eSAndroid Build Coastguard Worker  build_files_parser = subparsers.add_parser(
319*7594170eSAndroid Build Coastguard Worker      "build_files",
320*7594170eSAndroid Build Coastguard Worker      help="create a tar containing all bazel-related build files",
321*7594170eSAndroid Build Coastguard Worker  )
322*7594170eSAndroid Build Coastguard Worker  _define_global_flags(build_files_parser, True)
323*7594170eSAndroid Build Coastguard Worker  bp2build_parser = subparsers.add_parser(
324*7594170eSAndroid Build Coastguard Worker      "bp2build",
325*7594170eSAndroid Build Coastguard Worker      help="print whether a module was generated by bp2build",
326*7594170eSAndroid Build Coastguard Worker  )
327*7594170eSAndroid Build Coastguard Worker  _define_global_flags(bp2build_parser, True)
328*7594170eSAndroid Build Coastguard Worker  bp2build_parser.add_argument(
329*7594170eSAndroid Build Coastguard Worker      "module_names",
330*7594170eSAndroid Build Coastguard Worker      metavar="module_name",
331*7594170eSAndroid Build Coastguard Worker      nargs="*",
332*7594170eSAndroid Build Coastguard Worker      help="print conversion info about these modules",
333*7594170eSAndroid Build Coastguard Worker  )
334*7594170eSAndroid Build Coastguard Worker
335*7594170eSAndroid Build Coastguard Worker  args = parser.parse_args()
336*7594170eSAndroid Build Coastguard Worker
337*7594170eSAndroid Build Coastguard Worker  # Use `timing` as the default build mode.
338*7594170eSAndroid Build Coastguard Worker  if not args.mode:
339*7594170eSAndroid Build Coastguard Worker    args.mode = "timing"
340*7594170eSAndroid Build Coastguard Worker  # Check the metrics dir.
341*7594170eSAndroid Build Coastguard Worker  if not os.path.exists(args.metrics_files_dir):
342*7594170eSAndroid Build Coastguard Worker    raise Exception(
343*7594170eSAndroid Build Coastguard Worker        "Directory "
344*7594170eSAndroid Build Coastguard Worker        + arg.metrics_files_dir
345*7594170eSAndroid Build Coastguard Worker        + " not found. Did you run a build?"
346*7594170eSAndroid Build Coastguard Worker    )
347*7594170eSAndroid Build Coastguard Worker
348*7594170eSAndroid Build Coastguard Worker  args.output_dir = args.output_dir or _get_default_out_dir(
349*7594170eSAndroid Build Coastguard Worker      args.metrics_files_dir
350*7594170eSAndroid Build Coastguard Worker  )
351*7594170eSAndroid Build Coastguard Worker
352*7594170eSAndroid Build Coastguard Worker  if args.mode == "timing":
353*7594170eSAndroid Build Coastguard Worker    process_timing_mode(args)
354*7594170eSAndroid Build Coastguard Worker  elif args.mode == "build_files":
355*7594170eSAndroid Build Coastguard Worker    process_build_files_mode(args)
356*7594170eSAndroid Build Coastguard Worker  elif args.mode == "bp2build":
357*7594170eSAndroid Build Coastguard Worker    process_bp2build_mode(args)
358*7594170eSAndroid Build Coastguard Worker
359*7594170eSAndroid Build Coastguard Worker
360*7594170eSAndroid Build Coastguard Workerif __name__ == "__main__":
361*7594170eSAndroid Build Coastguard Worker  main()
362