• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright (C) 2022 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 os
17import sys
18import re
19import subprocess
20import pathlib
21import tempfile
22import contextlib
23import argparse
24
25ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
26TOOLS_DIR = os.path.join(ROOT_DIR, "tools")
27OUT_DIR = os.path.join(ROOT_DIR, "out", "tools")
28NINJA = os.path.join(TOOLS_DIR, "ninja")
29GN = os.path.join(TOOLS_DIR, "gn")
30PROTOC_PATH = os.path.join(OUT_DIR, "protoc")
31DESCRIPTOR_PATH = os.path.join(ROOT_DIR, "src", "trace_processor", "importers",
32                               "proto", "atoms.descriptor")
33PROTOBUF_BUILTINS_DIR = os.path.join(ROOT_DIR, "buildtools", "protobuf", "src")
34PROTO_LOGGING_URL = "https://android.googlesource.com/platform/frameworks/proto_logging.git"
35ATOM_RE = r"  message_type {\n.   name: \"Atom\"(\n    .+)+(\n  })"
36FIELD_RE = r"    field {\n      name: \"([^\"]+)\"\n      number: ([0-9]+)"
37ATOM_IDS_PATH = os.path.join(ROOT_DIR, "protos", "perfetto", "config", "statsd",
38                             "atom_ids.proto")
39ATOM_IDS_TEMPLATE = """/*
40 * Copyright (C) 2022 The Android Open Source Project
41 *
42 * Licensed under the Apache License, Version 2.0 (the "License");
43 * you may not use this file except in compliance with the License.
44 * You may obtain a copy of the License at
45 *
46 *      http://www.apache.org/licenses/LICENSE-2.0
47 *
48 * Unless required by applicable law or agreed to in writing, software
49 * distributed under the License is distributed on an "AS IS" BASIS,
50 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
51 * See the License for the specific language governing permissions and
52 * limitations under the License.
53 */
54syntax = "proto2";
55
56package perfetto.protos;
57
58// This enum is obtained by post-processing
59// AOSP/frameworks/proto_logging/stats/atoms.proto through
60// AOSP/external/perfetto/tools/update-statsd-descriptor, which extracts one
61// enum value for each proto field defined in the upstream atoms.proto.
62enum AtomId {{
63  ATOM_UNSPECIFIED = 0;
64{atoms}
65}}"""
66
67
68def call(*cmd, stdin=None):
69  try:
70    return subprocess.check_output(cmd, stdin=stdin)
71  except subprocess.CalledProcessError as e:
72    print("Error running the command:")
73    print(" ".join(cmd))
74    print(e)
75    exit(1)
76
77
78def main():
79  parser = argparse.ArgumentParser()
80  parser.add_argument("--atoms-checkout")
81  args = parser.parse_args()
82
83  call(GN, "gen", OUT_DIR, "--args=is_debug=false")
84  call(NINJA, "-C", OUT_DIR, "protoc")
85
86  with contextlib.ExitStack() as stack:
87
88    # Write the descriptor.
89    if args.atoms_checkout:
90      atoms_root = args.atoms_checkout
91      proto_logging_dir = os.path.join(atoms_root, "frameworks",
92                                       "proto_logging")
93    else:
94      atoms_root = stack.enter_context(tempfile.TemporaryDirectory())
95      proto_logging_dir = os.path.join(atoms_root, "frameworks",
96                                       "proto_logging")
97      pathlib.Path(proto_logging_dir).mkdir(parents=True, exist_ok=True)
98      call("git", "clone", PROTO_LOGGING_URL, proto_logging_dir)
99
100    atoms_path = os.path.join(proto_logging_dir, "stats", "atoms.proto")
101    call(PROTOC_PATH, f"--proto_path={PROTOBUF_BUILTINS_DIR}",
102         f"--proto_path={atoms_root}",
103         f"--descriptor_set_out={DESCRIPTOR_PATH}", "--include_imports",
104         atoms_path)
105
106    # Extract and update atom_ids.proto. To do this we regex the pbtext
107    # of the descriptor. This is hopefully:
108    # - more stable than regexing atom.proto directly
109    # - less complicated than parsing finding, importing, and using the
110    #   Python protobuf library.
111    descriptor_in = stack.enter_context(open(DESCRIPTOR_PATH))
112    pbtext = call(
113        PROTOC_PATH,
114        f"--proto_path={PROTOBUF_BUILTINS_DIR}",
115        f"{PROTOBUF_BUILTINS_DIR}/google/protobuf/descriptor.proto",
116        "--decode=google.protobuf.FileDescriptorSet",
117        stdin=descriptor_in)
118
119    atom_pbtext = re.search(ATOM_RE, pbtext.decode("utf8"), re.MULTILINE)[0]
120
121    lines = []
122    for m in re.finditer(FIELD_RE, atom_pbtext):
123      name = "ATOM_" + m[1].upper()
124      field = m[2]
125      lines.append(f"  {name} = {field};".format(name=name, field=field))
126    atom_ids_out = stack.enter_context(open(ATOM_IDS_PATH, "w"))
127    atom_ids_out.write(ATOM_IDS_TEMPLATE.format(atoms="\n".join(lines)))
128
129
130if __name__ == "__main__":
131  sys.exit(main())
132