xref: /aosp_15_r20/external/perfetto/tools/update_sql_parsers.py (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1#!/usr/bin/env python3
2# Copyright (C) 2020 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 os
18import subprocess
19import shutil
20import sys
21import tempfile
22
23GRAMMAR_FOOTER = '''
24%token SPACE ILLEGAL.
25'''
26
27KEYWORDHASH_HEADER = '''
28#include "src/trace_processor/perfetto_sql/grammar/perfettosql_keywordhash_helper.h"
29'''
30
31KEYWORD_END = '''
32  { "WITHOUT",          "TK_WITHOUT",      ALWAYS,           1      },
33};'''
34
35KEYWORD_END_REPLACE = '''
36  { "WITHOUT",          "TK_WITHOUT",      ALWAYS,           1      },
37  { "PERFETTO",         "TK_PERFETTO",     ALWAYS,           1      },
38  { "MACRO",            "TK_MACRO",        ALWAYS,           1      },
39  { "INCLUDE",          "TK_INCLUDE",      ALWAYS,           1      },
40  { "MODULE",           "TK_MODULE",       ALWAYS,           1      },
41  { "RETURNS",          "TK_RETURNS",      ALWAYS,           1      },
42  { "FUNCTION",         "TK_FUNCTION",     ALWAYS,           1      },
43};'''
44
45
46def copy_tokenizer(args: argparse.Namespace):
47  shutil.copy(args.sqlite_tokenize, args.sqlite_tokenize_out)
48
49  with open(args.sqlite_tokenize_out, 'r+', encoding='utf-8') as fp:
50    res: str = fp.read()
51    idx = res.find('/*\n** Run the parser on the given SQL string.')
52    assert idx != -1
53    res = res[0:idx]
54    res = res.replace(
55        '#include "sqliteInt.h"',
56        '#include "src/trace_processor/perfetto_sql/tokenizer/tokenize_internal_helper.h"',
57    )
58    res = res.replace('#include "keywordhash.h"\n', '')
59    fp.seek(0)
60    fp.write(res)
61    fp.truncate()
62
63
64def main():
65  parser = argparse.ArgumentParser()
66  parser.add_argument(
67      '--lemon', default=os.path.normpath('buildtools/sqlite_src/tool/lemon.c'))
68  parser.add_argument(
69      '--mkkeywordhash',
70      default=os.path.normpath('buildtools/sqlite_src/tool/mkkeywordhash.c'))
71  parser.add_argument(
72      '--lemon-template',
73      default=os.path.normpath('buildtools/sqlite_src/tool/lempar.c'))
74  parser.add_argument(
75      '--clang', default=os.path.normpath('buildtools/linux64/clang/bin/clang'))
76  parser.add_argument(
77      '--preprocessor-grammar',
78      default=os.path.normpath(
79          'src/trace_processor/perfetto_sql/preprocessor/preprocessor_grammar.y'
80      ),
81  )
82  parser.add_argument(
83      '--sqlite-grammar',
84      default=os.path.normpath('buildtools/sqlite_src/src/parse.y'),
85  )
86  parser.add_argument(
87      '--perfettosql-grammar-include',
88      default=os.path.normpath(
89          'src/trace_processor/perfetto_sql/grammar/perfettosql_include.y'),
90  )
91  parser.add_argument(
92      '--grammar-out',
93      default=os.path.join(
94          os.path.normpath('src/trace_processor/perfetto_sql/grammar/')),
95  )
96  parser.add_argument(
97      '--sqlite-tokenize',
98      default=os.path.normpath('buildtools/sqlite_src/src/tokenize.c'),
99  )
100  parser.add_argument(
101      '--sqlite-tokenize-out',
102      default=os.path.join(
103          os.path.normpath(
104              'src/trace_processor/perfetto_sql/tokenizer/tokenize_internal.c')
105      ),
106  )
107  args = parser.parse_args()
108
109  with tempfile.TemporaryDirectory() as tmp:
110    # Preprocessor grammar
111    subprocess.check_call([
112        args.clang,
113        os.path.join(args.lemon), '-o',
114        os.path.join(tmp, 'lemon')
115    ])
116    shutil.copy(args.lemon_template, tmp)
117    subprocess.check_call([
118        os.path.join(tmp, 'lemon'),
119        args.preprocessor_grammar,
120        '-q',
121        '-l',
122        '-s',
123    ])
124
125    # PerfettoSQL keywords
126    keywordhash_tmp = os.path.join(tmp, 'mkkeywordhash.c')
127    shutil.copy(args.mkkeywordhash, keywordhash_tmp)
128
129    with open(keywordhash_tmp, "r+") as fp:
130      keyword_source = fp.read()
131      assert keyword_source.find(KEYWORD_END) != -1
132      fp.seek(0)
133      fp.write(keyword_source.replace(KEYWORD_END, KEYWORD_END_REPLACE))
134      fp.truncate()
135
136    subprocess.check_call([
137        args.clang,
138        os.path.join(keywordhash_tmp), '-o',
139        os.path.join(tmp, 'mkkeywordhash')
140    ])
141    keywordhash_res = subprocess.check_output(
142        [os.path.join(tmp, 'mkkeywordhash')]).decode()
143
144    with open(os.path.join(args.grammar_out, "perfettosql_keywordhash.h"),
145              "w") as g:
146      idx = keywordhash_res.find('#define SQLITE_N_KEYWORD')
147      assert idx != -1
148      keywordhash_res = keywordhash_res[0:idx]
149      g.write(KEYWORDHASH_HEADER)
150      g.write(keywordhash_res)
151
152    # PerfettoSQL grammar
153    sqlite_grammar = subprocess.check_output([
154        os.path.join(tmp, 'lemon'),
155        args.sqlite_grammar,
156        '-g',
157    ]).decode()
158    with open(os.path.join(args.grammar_out, "perfettosql_grammar.y"),
159              "w") as g:
160      with open(args.perfettosql_grammar_include, 'r') as i:
161        g.write(i.read())
162      g.write(sqlite_grammar)
163      g.write(GRAMMAR_FOOTER)
164    subprocess.check_call([
165        os.path.join(tmp, 'lemon'),
166        os.path.join(args.grammar_out, "perfettosql_grammar.y"),
167        '-q',
168        '-l',
169        '-s',
170    ])
171
172  copy_tokenizer(args)
173
174  return 0
175
176
177if __name__ == '__main__':
178  sys.exit(main())
179