xref: /aosp_15_r20/external/perfetto/tools/gen_tp_table_docs.py (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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 argparse
17import json
18import os
19import sys
20from typing import Any
21from typing import Dict
22from typing import Optional
23
24# Allow importing of root-relative modules.
25ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
26sys.path.append(os.path.join(ROOT_DIR))
27
28#pylint: disable=wrong-import-position
29from python.generators.trace_processor_table.public import ColumnDoc
30from python.generators.trace_processor_table.public import ColumnFlag
31import python.generators.trace_processor_table.util as util
32from python.generators.trace_processor_table.util import ParsedTable
33from python.generators.trace_processor_table.util import ParsedColumn
34#pylint: enable=wrong-import-position
35
36
37def gen_json_for_column(table: ParsedTable,
38                        col: ParsedColumn) -> Optional[Dict[str, Any]]:
39  """Generates the JSON documentation for a column in a table."""
40  assert table.table.tabledoc
41
42  # id and type columns should be skipped if the table specifies so.
43  is_skippable_col = col.is_implicit_id or col.is_implicit_type
44  if table.table.tabledoc.skip_id_and_type and is_skippable_col:
45    return None
46
47  # Ignore hidden columns in the documentation.
48  if ColumnFlag.HIDDEN in col.column.flags:
49    return None
50
51  # Our default assumption is the documentation for a column is a plain string
52  # so just make the comment for the column equal to that.
53
54  if isinstance(col.doc, ColumnDoc):
55    comment = col.doc.doc
56    if col.doc.joinable:
57      join_table, join_col = col.doc.joinable.split('.')
58    else:
59      join_table, join_col = None, None
60  elif isinstance(col.doc, str):
61    comment = col.doc
62    join_table, join_col = None, None
63  else:
64    raise Exception('Unknown column documentation type '
65                    f'{table.table.class_name}::{col.column.name}')
66
67  parsed_type = util.parse_type_with_cols(table.table,
68                                          [c.column for c in table.columns],
69                                          col.column.type)
70  docs_type = parsed_type.cpp_type
71  if docs_type == 'StringPool::Id':
72    docs_type = 'string'
73
74  ref_class_name = None
75  if parsed_type.id_table and not col.is_implicit_id:
76    id_table_name = util.public_sql_name(parsed_type.id_table)
77    ref_class_name = parsed_type.id_table.class_name
78
79    if not join_table and not join_col:
80      join_table = id_table_name
81      join_col = "id"
82
83  return {
84      'name': col.column.name,
85      'type': docs_type,
86      'comment': comment,
87      'optional': parsed_type.is_optional,
88      'refTableCppName': ref_class_name,
89      'joinTable': join_table,
90      'joinCol': join_col,
91  }
92
93
94def main():
95  parser = argparse.ArgumentParser()
96  parser.add_argument('--out', required=True)
97  parser.add_argument('inputs', nargs='*')
98  parser.add_argument('--relative-input-dir')
99  args = parser.parse_args()
100
101  def get_relin_path(in_path: str):
102    if not args.relative_input_dir:
103      return in_path
104    return os.path.relpath(in_path, args.relative_input_dir)
105
106  modules = [
107      os.path.splitext(get_relin_path(i).replace('/', '.'))[0]
108      for i in args.inputs
109  ]
110  table_docs = []
111  for parsed in util.parse_tables_from_modules(modules):
112    table = parsed.table
113
114    # If there is no non-intrinsic alias for the table, don't
115    # include the table in the docs.
116    name = util.public_sql_name(table)
117    if name.startswith('__intrinsic_') or name.startswith('experimental_'):
118      continue
119
120    doc = table.tabledoc
121    assert doc
122    cols = (
123        gen_json_for_column(parsed, c)
124        for c in parsed.columns
125        if not c.is_ancestor)
126    table_docs.append({
127        'name': name,
128        'cppClassName': table.class_name,
129        'defMacro': table.class_name,
130        'comment': '\n'.join(l.strip() for l in doc.doc.splitlines()),
131        'parent': None,
132        'parentDefName': table.parent.class_name if table.parent else '',
133        'tablegroup': doc.group,
134        'cols': [c for c in cols if c]
135    })
136
137  with open(args.out, 'w') as out:
138    json.dump(table_docs, out, indent=2)
139    out.write('\n')
140
141
142if __name__ == '__main__':
143  exit(main())
144