xref: /aosp_15_r20/external/perfetto/infra/perfetto.dev/src/gen_sql_tables_reference.js (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2020 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Generation of SQL table references from C++ headers.
16
17'use strict';
18
19const fs = require('fs');
20const argv = require('yargs').argv
21
22// Removes \n due to 80col wrapping and preserves only end-of-sentence line
23// breaks.
24// TODO dedupe, this is copied from the other gen_proto file.
25function singleLineComment(comment) {
26  comment = comment || '';
27  comment = comment.trim();
28  comment = comment.replaceAll('|', '\\|');
29  comment = comment.replace(/\.\n/g, '<br>');
30  comment = comment.replace(/\n/g, ' ');
31  return comment;
32}
33
34function parseTablesInJson(filePath) {
35  return JSON.parse(fs.readFileSync(filePath, 'UTF8'));
36}
37
38function genLink(table) {
39  return `[${table.name}](#${table.name})`;
40}
41
42function tableToMarkdown(table) {
43  let md = `### ${table.name}\n\n`;
44  if (table.parent) {
45    md += `_Extends ${genLink(table.parent)}_\n\n`;
46  }
47  md += table.comment + '\n\n';
48  md += 'Column | Type | Description\n';
49  md += '------ | ---- | -----------\n';
50
51  let curTable = table;
52  while (curTable) {
53    if (curTable != table) {
54      md += `||_Columns inherited from_ ${genLink(curTable)}\n`
55    }
56    for (const col of Object.values(curTable.cols)) {
57      const type = col.type + (col.optional ? '<br>`optional`' : '');
58      let description = col.comment;
59      if (col.joinTable) {
60        description += `\nJoinable with ` +
61            `[${col.joinTable}.${col.joinCol}](#${col.joinTable})`;
62      }
63      md += `${col.name} | ${type} | ${singleLineComment(description)}\n`
64    }
65    curTable = curTable.parent;
66  }
67  md += '\n\n';
68  return md;
69}
70
71function main() {
72  const outFile = argv['o'];
73  const jsonFile = argv['j'];
74  if (!jsonFile) {
75    console.error('Usage: -j tbls.json -[-o out.md]');
76    process.exit(1);
77  }
78
79  // Can be either a string (-j single) or an array (-j one -j two).
80  const jsonFiles = (jsonFile instanceof Array) ? jsonFile : [jsonFile];
81  const jsonTables =
82      Array.prototype.concat(...jsonFiles.map(parseTablesInJson));
83
84  // Resolve parents.
85  const tablesIndex = {};    // 'TP_SCHED_SLICE_TABLE_DEF' -> table
86  const tablesByGroup = {};  // 'profilers' => [table1, table2]
87  const tablesCppName = {};  // 'StackProfileMappingTable' => table
88  const tablesByName = {};   // 'profile_mapping' => table
89  for (const table of jsonTables) {
90    tablesIndex[table.defMacro] = table;
91    if (tablesByGroup[table.tablegroup] === undefined) {
92      tablesByGroup[table.tablegroup] = [];
93    }
94    tablesCppName[table.cppClassName] = table;
95    tablesByName[table.name] = table;
96    tablesByGroup[table.tablegroup].push(table);
97  }
98  const tableGroups = Object.keys(tablesByGroup).sort((a, b) => {
99    const keys = {'Tracks': '1', 'Events': '2', 'Misc': 'z'};
100    a = `${keys[a]}_${a}`;
101    b = `${keys[b]}_${b}`;
102    return a.localeCompare(b);
103  });
104
105  for (const table of jsonTables) {
106    if (table.parentDefName) {
107      table.parent = tablesIndex[table.parentDefName];
108    }
109  }
110
111  // Builds a graph of the tables' relationship that can be rendererd with
112  // mermaid.js.
113  let graph = '## Tables diagram\n';
114  const mkLabel = (table) => `${table.defMacro}["${table.name}"]`;
115  for (const tableGroup of tableGroups) {
116    let graphEdges = '';
117    let graphLinks = '';
118    graph += `#### ${tableGroup} tables\n`;
119    graph += '```mermaid\ngraph TD\n';
120    graph += `  subgraph ${tableGroup}\n`;
121    for (const table of tablesByGroup[tableGroup]) {
122      graph += `  ${mkLabel(table)}\n`;
123      graphLinks += `  click ${table.defMacro} "#${table.name}"\n`
124      if (table.parent) {
125        graphEdges += ` ${mkLabel(table)} --> ${mkLabel(table.parent)}\n`
126      }
127
128      for (const col of Object.values(table.cols)) {
129        let refTable = undefined;
130        if (col.refTableCppName) {
131          refTable = tablesCppName[col.refTableCppName];
132        } else if (col.joinTable) {
133          refTable = tablesByName[col.joinTable];
134          if (!refTable) {
135            throw new Error(`Cannot find @joinable table ${col.joinTable}`);
136          }
137        }
138        if (!refTable)
139          continue;
140        graphEdges +=
141            `  ${mkLabel(table)} -. ${col.name} .-> ${mkLabel(refTable)}\n`
142        graphLinks += `  click ${refTable.defMacro} "#${refTable.name}"\n`
143      }
144    }
145    graph += `  end\n`;
146    graph += graphEdges;
147    graph += graphLinks;
148    graph += '\n```\n';
149  }
150
151  let title = '# PerfettoSQL Prelude\n'
152  let md = title + graph;
153  for (const tableGroup of tableGroups) {
154    md += `## ${tableGroup}\n`
155    for (const table of tablesByGroup[tableGroup]) {
156      md += tableToMarkdown(table);
157    }
158  }
159
160  if (outFile) {
161    fs.writeFileSync(outFile, md);
162  } else {
163    console.log(md);
164  }
165  process.exit(0);
166}
167
168main();
169