1/****************************************************************************
2**
3** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto <rafael.roquetto@kdab.com>
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "provider.h"
41#include "panic.h"
42
43#include <qfile.h>
44#include <qfileinfo.h>
45#include <qtextstream.h>
46#include <qregexp.h>
47#include <qstring.h>
48
49#ifdef TRACEGEN_DEBUG
50#include <qdebug.h>
51
52static void dumpTracepoint(const Tracepoint &t)
53{
54 qDebug() << "=== BEGIN TRACEPOINT ===";
55 qDebug() << "name:" << t.name;
56 qDebug() << "ARGS\n";
57
58 int j = 0;
59
60 for (auto i = t.args.constBegin(); i != t.args.constEnd(); ++i) {
61 qDebug() << "ARG[" << j << "] type:" << i->type;
62 qDebug() << "ARG[" << j << "] name:" << i->name;
63 qDebug() << "ARG[" << j << "] arrayLen:" << i->arrayLen;
64 ++j;
65 }
66
67 qDebug() << "\nFIELDS\n";
68
69 j = 0;
70
71 for (auto i = t.fields.constBegin(); i != t.fields.constEnd(); ++i) {
72 qDebug() << "FIELD[" << j << "] backend_type" << static_cast<int>(i->backendType);
73 qDebug() << "FIELD[" << j << "] param_type" << i->paramType;
74 qDebug() << "FIELD[" << j << "] name" << i->name;
75 qDebug() << "FIELD[" << j << "] arrayLen" << i->arrayLen;
76 qDebug() << "FIELD[" << j << "] seqLen" << i->seqLen;
77 ++j;
78 }
79
80 qDebug() << "=== END TRACEPOINT ===\n";
81
82}
83#endif
84
85static inline int arrayLength(const QString &rawType)
86{
87 /* matches the length of an ordinary array type
88 * Ex: foo[10] yields '10'
89 */
90 static const QRegExp rx(QStringLiteral(".*\\[([0-9]+)\\].*"));
91
92 if (!rx.exactMatch(str: rawType.trimmed()))
93 return 0;
94
95 return rx.cap(nth: 1).toInt();
96}
97
98static inline QString sequenceLength(const QString &rawType)
99{
100 /* matches the identifier pointing to length of a CTF sequence type, which is
101 * a dynamic sized array.
102 * Ex: in qcoreapplication_foo(const char[len], some_string, unsigned int, * len)
103 * it will match the 'len' part of 'const char[len]')
104 */
105 static const QRegExp rx(QStringLiteral(".*\\[([A-Za-z_][A-Za-z_0-9]*)\\].*"));
106
107 if (!rx.exactMatch(str: rawType.trimmed()))
108 return QString();
109
110 return rx.cap(nth: 1);
111}
112
113static QString decayArrayToPointer(QString type)
114{
115 /* decays an array to a pointer, i.e., if 'type' holds int[10],
116 * this function returns 'int *'
117 */
118 static QRegExp rx(QStringLiteral("\\[(.+)\\]"));
119
120 rx.setMinimal(true);
121 return type.replace(rx, QStringLiteral("*"));
122}
123
124static QString removeBraces(QString type)
125{
126 static const QRegExp rx(QStringLiteral("\\[.*\\]"));
127
128 return type.remove(rx);
129}
130
131static Tracepoint::Field::BackendType backendType(QString rawType)
132{
133 static const struct {
134 const char *type;
135 Tracepoint::Field::BackendType backendType;
136 } typeTable[] = {
137 { .type: "bool", .backendType: Tracepoint::Field::Integer },
138 { .type: "short_int", .backendType: Tracepoint::Field::Integer },
139 { .type: "signed_short", .backendType: Tracepoint::Field::Integer },
140 { .type: "signed_short_int", .backendType: Tracepoint::Field::Integer },
141 { .type: "unsigned_short", .backendType: Tracepoint::Field::Integer },
142 { .type: "unsigned_short_int", .backendType: Tracepoint::Field::Integer },
143 { .type: "int", .backendType: Tracepoint::Field::Integer },
144 { .type: "signed", .backendType: Tracepoint::Field::Integer },
145 { .type: "signed_int", .backendType: Tracepoint::Field::Integer },
146 { .type: "unsigned", .backendType: Tracepoint::Field::Integer },
147 { .type: "unsigned_int", .backendType: Tracepoint::Field::Integer },
148 { .type: "long", .backendType: Tracepoint::Field::Integer },
149 { .type: "long_int", .backendType: Tracepoint::Field::Integer },
150 { .type: "signed_long", .backendType: Tracepoint::Field::Integer },
151 { .type: "signed_long_int", .backendType: Tracepoint::Field::Integer },
152 { .type: "unsigned_long", .backendType: Tracepoint::Field::Integer },
153 { .type: "unsigned_long_int", .backendType: Tracepoint::Field::Integer },
154 { .type: "long_long", .backendType: Tracepoint::Field::Integer },
155 { .type: "long_long_int", .backendType: Tracepoint::Field::Integer },
156 { .type: "signed_long_long", .backendType: Tracepoint::Field::Integer },
157 { .type: "signed_long_long_int", .backendType: Tracepoint::Field::Integer },
158 { .type: "unsigned_long_long", .backendType: Tracepoint::Field::Integer },
159 { .type: "char", .backendType: Tracepoint::Field::Integer },
160 { .type: "intptr_t", .backendType: Tracepoint::Field::IntegerHex },
161 { .type: "uintptr_t", .backendType: Tracepoint::Field::IntegerHex },
162 { .type: "std::intptr_t", .backendType: Tracepoint::Field::IntegerHex },
163 { .type: "std::uintptr_t", .backendType: Tracepoint::Field::IntegerHex },
164 { .type: "float", .backendType: Tracepoint::Field::Float },
165 { .type: "double", .backendType: Tracepoint::Field::Float },
166 { .type: "long_double", .backendType: Tracepoint::Field::Float },
167 { .type: "QString", .backendType: Tracepoint::Field::QtString },
168 { .type: "QByteArray", .backendType: Tracepoint::Field::QtByteArray },
169 { .type: "QUrl", .backendType: Tracepoint::Field::QtUrl },
170 { .type: "QRect", .backendType: Tracepoint::Field::QtRect }
171 };
172
173 auto backendType = [](const QString &rawType) {
174 static const size_t tableSize = sizeof (typeTable) / sizeof (typeTable[0]);
175
176 for (size_t i = 0; i < tableSize; ++i) {
177 if (rawType == QLatin1String(typeTable[i].type))
178 return typeTable[i].backendType;
179 }
180
181 return Tracepoint::Field::Unknown;
182 };
183
184 if (arrayLength(rawType) > 0)
185 return Tracepoint::Field::Array;
186
187 if (!sequenceLength(rawType).isNull())
188 return Tracepoint::Field::Sequence;
189
190 static const QRegExp constMatch(QStringLiteral("\\bconst\\b"));
191 rawType.remove(rx: constMatch);
192 rawType.remove(c: QLatin1Char('&'));
193
194 static const QRegExp ptrMatch(QStringLiteral("\\s*\\*\\s*"));
195 rawType.replace(rx: ptrMatch, QStringLiteral("_ptr"));
196 rawType = rawType.trimmed();
197 rawType.replace(QStringLiteral(" "), QStringLiteral("_"));
198
199 if (rawType == QLatin1String("char_ptr"))
200 return Tracepoint::Field::String;
201
202 if (rawType.endsWith(s: QLatin1String("_ptr")))
203 return Tracepoint::Field::Pointer;
204
205 return backendType(rawType);
206}
207
208static Tracepoint parseTracepoint(const QString &name, const QStringList &args,
209 const QString &fileName, const int lineNumber)
210{
211 Tracepoint t;
212 t.name = name;
213
214 if (args.isEmpty())
215 return t;
216
217 auto i = args.constBegin();
218 auto end = args.constEnd();
219 int argc = 0;
220
221 static const QRegExp rx(QStringLiteral("(.*)\\b([A-Za-z_][A-Za-z0-9_]*)$"));
222
223 while (i != end) {
224 rx.exactMatch(str: *i);
225
226 const QString type = rx.cap(nth: 1).trimmed();
227
228 if (type.isNull()) {
229 panic(fmt: "Missing parameter type for argument %d of %s (%s:%d)",
230 argc, qPrintable(name), qPrintable(fileName), lineNumber);
231 }
232
233 const QString name = rx.cap(nth: 2).trimmed();
234
235 if (name.isNull()) {
236 panic(fmt: "Missing parameter name for argument %d of %s (%s:%d)",
237 argc, qPrintable(name), qPrintable(fileName), lineNumber);
238 }
239
240 int arrayLen = arrayLength(rawType: type);
241
242 Tracepoint::Argument a;
243 a.arrayLen = arrayLen;
244 a.name = name;
245 a.type = decayArrayToPointer(type);
246
247 t.args << std::move(a);
248
249 Tracepoint::Field f;
250 f.backendType = backendType(rawType: type);
251 f.paramType = removeBraces(type);
252 f.name = name;
253 f.arrayLen = arrayLen;
254 f.seqLen = sequenceLength(rawType: type);
255
256 t.fields << std::move(f);
257
258 ++i;
259 }
260
261 return t;
262}
263
264Provider parseProvider(const QString &filename)
265{
266 QFile f(filename);
267
268 if (!f.open(flags: QIODevice::ReadOnly | QIODevice::Text))
269 panic(fmt: "Cannot open %s: %s", qPrintable(filename), qPrintable(f.errorString()));
270
271 QTextStream s(&f);
272
273 static const QRegExp tracedef(QStringLiteral("([A-Za-z][A-Za-z0-9_]*)\\((.*)\\)"));
274
275 Provider provider;
276 provider.name = QFileInfo(filename).baseName();
277
278 bool parsingPrefixText = false;
279 for (int lineNumber = 1; !s.atEnd(); ++lineNumber) {
280 QString line = s.readLine().trimmed();
281
282 if (line == QLatin1String("{")) {
283 parsingPrefixText = true;
284 continue;
285 } else if (parsingPrefixText && line == QLatin1String("}")) {
286 parsingPrefixText = false;
287 continue;
288 } else if (parsingPrefixText) {
289 provider.prefixText.append(t: line);
290 continue;
291 }
292
293 if (line.isEmpty() || line.startsWith(c: QLatin1Char('#')))
294 continue;
295
296 if (tracedef.exactMatch(str: line)) {
297 const QString name = tracedef.cap(nth: 1);
298 const QString argsString = tracedef.cap(nth: 2);
299 const QStringList args = argsString.split(sep: QLatin1Char(','), behavior: Qt::SkipEmptyParts);
300
301 provider.tracepoints << parseTracepoint(name, args, fileName: filename, lineNumber);
302 } else {
303 panic(fmt: "Syntax error while processing '%s' line %d:\n"
304 " '%s' does not look like a tracepoint definition",
305 qPrintable(filename), lineNumber,
306 qPrintable(line));
307 }
308 }
309
310 if (parsingPrefixText) {
311 panic(fmt: "Syntax error while processing '%s': "
312 "no closing brace found for prefix text block",
313 qPrintable(filename));
314 }
315
316#ifdef TRACEGEN_DEBUG
317 qDebug() << provider.prefixText;
318 for (auto i = provider.tracepoints.constBegin(); i != provider.tracepoints.constEnd(); ++i)
319 dumpTracepoint(*i);
320#endif
321
322 return provider;
323}
324

source code of qtbase/src/tools/tracegen/provider.cpp