• 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
16# This tool checks that every SQL object created without prefix
17# '_' is documented with proper schema.
18
19import argparse
20from typing import List, Tuple
21import os
22import sys
23import re
24
25ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
26sys.path.append(os.path.join(ROOT_DIR))
27
28from python.generators.sql_processing.docs_parse import ParsedFile
29from python.generators.sql_processing.docs_parse import parse_file
30from python.generators.sql_processing.utils import check_banned_create_table_as
31from python.generators.sql_processing.utils import check_banned_create_view_as
32from python.generators.sql_processing.utils import check_banned_words
33from python.generators.sql_processing.utils import check_banned_include_all
34
35# Allowlist path are relative to the stdlib root.
36CREATE_TABLE_ALLOWLIST = {
37    '/prelude/trace_bounds.sql': ['trace_bounds'],
38    '/android/binder.sql': ['_oom_score'],
39    '/android/monitor_contention.sql': [
40        '_isolated', 'android_monitor_contention_chain',
41        'android_monitor_contention'
42    ],
43    '/chrome/tasks.sql': [
44        '_chrome_mojo_slices', '_chrome_java_views', '_chrome_scheduler_tasks',
45        '_chrome_tasks'
46    ],
47    '/sched/thread_executing_span.sql': [
48        '_wakeup_graph', '_thread_executing_span_graph', '_critical_path'
49    ],
50    '/slices/flat_slices.sql': ['_slice_flattened'],
51    '/wattson/curves/utils.sql': [
52        '_filtered_curves_1d', '_filtered_curves_2d', '_filtered_curves_l3'
53    ],
54}
55
56
57def main():
58  parser = argparse.ArgumentParser()
59  parser.add_argument(
60      '--stdlib-sources',
61      default=os.path.join(ROOT_DIR, "src", "trace_processor", "perfetto_sql",
62                           "stdlib"))
63  parser.add_argument(
64      '--verbose',
65      action='store_true',
66      default=False,
67      help='Enable additional logging')
68  parser.add_argument(
69      '--name-filter',
70      default=None,
71      type=str,
72      help='Filter the name of the modules to check (regex syntax)')
73
74  args = parser.parse_args()
75  errors = []
76  modules: List[Tuple[str, str, ParsedFile]] = []
77  for root, _, files in os.walk(args.stdlib_sources, topdown=True):
78    for f in files:
79      path = os.path.join(root, f)
80      if not path.endswith(".sql"):
81        continue
82      rel_path = os.path.relpath(path, args.stdlib_sources)
83      if args.name_filter is not None:
84        pattern = re.compile(args.name_filter)
85        if not pattern.match(rel_path):
86          continue
87
88      with open(path, 'r') as f:
89        sql = f.read()
90
91      parsed = parse_file(rel_path, sql)
92
93      # Some modules (i.e. `deprecated`) should not be checked.
94      if not parsed:
95        continue
96
97      modules.append((path, sql, parsed))
98
99      if args.verbose:
100        obj_count = len(parsed.functions) + len(parsed.table_functions) + len(
101            parsed.table_views) + len(parsed.macros)
102        print(
103            f"""Parsing '{rel_path}' ({obj_count} objects, {len(parsed.errors)} errors)
104- {len(parsed.functions)} functions + {len(parsed.table_functions)} table functions,
105- {len(parsed.table_views)} tables/views,
106- {len(parsed.macros)} macros.""")
107
108  for path, sql, parsed in modules:
109    lines = [l.strip() for l in sql.split('\n')]
110    for line in lines:
111      if line.startswith('--'):
112        continue
113      if 'RUN_METRIC' in line:
114        errors.append(f"RUN_METRIC is banned in standard library.\n"
115                      f"Offending file: {path}\n")
116      if 'include perfetto module common.' in line.casefold():
117        errors.append(
118            f"Common module has been deprecated in the standard library.\n"
119            f"Offending file: {path}\n")
120      if 'insert into' in line.casefold():
121        errors.append(f"INSERT INTO table is not allowed in standard library.\n"
122                      f"Offending file: {path}\n")
123
124    errors += parsed.errors
125    errors += check_banned_words(sql, path)
126    errors += check_banned_create_table_as(
127        sql,
128        path.split(ROOT_DIR)[1],
129        args.stdlib_sources.split(ROOT_DIR)[1], CREATE_TABLE_ALLOWLIST)
130    errors += check_banned_create_view_as(sql, path.split(ROOT_DIR)[1])
131    errors += check_banned_include_all(sql, path.split(ROOT_DIR)[1])
132
133  if errors:
134    sys.stderr.write("\n".join(errors))
135    sys.stderr.write("\n")
136  return 0 if not errors else 1
137
138
139if __name__ == "__main__":
140  sys.exit(main())
141