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 <qregularexpression.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 QRegularExpression rx(QStringLiteral("\\[([0-9]+)\\]"));
91
92 auto match = rx.match(rawType);
93 if (!match.hasMatch())
94 return 0;
95
96 return match.captured(1).toInt();
97}
98
99static inline QString sequenceLength(const QString &rawType)
100{
101 /* matches the identifier pointing to length of a CTF sequence type, which is
102 * a dynamic sized array.
103 * Ex: in qcoreapplication_foo(const char[len], some_string, unsigned int, * len)
104 * it will match the 'len' part of 'const char[len]')
105 */
106 static const QRegularExpression rx(QStringLiteral("\\[([A-Za-z_][A-Za-z_0-9]*)\\]"));
107
108 auto match = rx.match(rawType);
109 if (!match.hasMatch())
110 return QString();
111
112 return match.captured(1);
113}
114
115static QString decayArrayToPointer(QString type)
116{
117 /* decays an array to a pointer, i.e., if 'type' holds int[10],
118 * this function returns 'int *'
119 */
120 static QRegularExpression rx(QStringLiteral("\\[([^\\]]+)\\]"));
121
122 return type.replace(rx, QStringLiteral("*"));
123}
124
125static QString removeBraces(QString type)
126{
127 static const QRegularExpression rx(QStringLiteral("\\[.*\\]"));
128
129 return type.remove(rx);
130}
131
132static Tracepoint::Field::BackendType backendType(QString rawType)
133{
134 static const struct {
135 const char *type;
136 Tracepoint::Field::BackendType backendType;
137 } typeTable[] = {
138 { "bool", Tracepoint::Field::Integer },
139 { "short_int", Tracepoint::Field::Integer },
140 { "signed_short", Tracepoint::Field::Integer },
141 { "signed_short_int", Tracepoint::Field::Integer },
142 { "unsigned_short", Tracepoint::Field::Integer },
143 { "unsigned_short_int", Tracepoint::Field::Integer },
144 { "int", Tracepoint::Field::Integer },
145 { "signed", Tracepoint::Field::Integer },
146 { "signed_int", Tracepoint::Field::Integer },
147 { "unsigned", Tracepoint::Field::Integer },
148 { "unsigned_int", Tracepoint::Field::Integer },
149 { "long", Tracepoint::Field::Integer },
150 { "long_int", Tracepoint::Field::Integer },
151 { "signed_long", Tracepoint::Field::Integer },
152 { "signed_long_int", Tracepoint::Field::Integer },
153 { "unsigned_long", Tracepoint::Field::Integer },
154 { "unsigned_long_int", Tracepoint::Field::Integer },
155 { "long_long", Tracepoint::Field::Integer },
156 { "long_long_int", Tracepoint::Field::Integer },
157 { "signed_long_long", Tracepoint::Field::Integer },
158 { "signed_long_long_int", Tracepoint::Field::Integer },
159 { "unsigned_long_long", Tracepoint::Field::Integer },
160 { "char", Tracepoint::Field::Integer },
161 { "intptr_t", Tracepoint::Field::IntegerHex },
162 { "uintptr_t", Tracepoint::Field::IntegerHex },
163 { "std::intptr_t", Tracepoint::Field::IntegerHex },
164 { "std::uintptr_t", Tracepoint::Field::IntegerHex },
165 { "float", Tracepoint::Field::Float },
166 { "double", Tracepoint::Field::Float },
167 { "long_double", Tracepoint::Field::Float },
168 { "QString", Tracepoint::Field::QtString },
169 { "QByteArray", Tracepoint::Field::QtByteArray },
170 { "QUrl", Tracepoint::Field::QtUrl },
171 { "QRect", Tracepoint::Field::QtRect }
172 };
173
174 auto backendType = [](const QString &rawType) {
175 static const size_t tableSize = sizeof (typeTable) / sizeof (typeTable[0]);
176
177 for (size_t i = 0; i < tableSize; ++i) {
178 if (rawType == QLatin1String(typeTable[i].type))
179 return typeTable[i].backendType;
180 }
181
182 return Tracepoint::Field::Unknown;
183 };
184
185 if (arrayLength(rawType) > 0)
186 return Tracepoint::Field::Array;
187
188 if (!sequenceLength(rawType).isNull())
189 return Tracepoint::Field::Sequence;
190
191 static const QRegularExpression constMatch(QStringLiteral("\\bconst\\b"));
192 rawType.remove(constMatch);
193 rawType.remove(QLatin1Char('&'));
194
195 static const QRegularExpression ptrMatch(QStringLiteral("\\s*\\*\\s*"));
196 rawType.replace(ptrMatch, QStringLiteral("_ptr"));
197 rawType = rawType.trimmed();
198 rawType.replace(QStringLiteral(" "), QStringLiteral("_"));
199
200 if (rawType == QLatin1String("char_ptr"))
201 return Tracepoint::Field::String;
202
203 if (rawType.endsWith(QLatin1String("_ptr")))
204 return Tracepoint::Field::Pointer;
205
206 return backendType(rawType);
207}
208
209static Tracepoint parseTracepoint(const QString &name, const QStringList &args,
210 const QString &fileName, const int lineNumber)
211{
212 Tracepoint t;
213 t.name = name;
214
215 if (args.isEmpty())
216 return t;
217
218 auto i = args.constBegin();
219 auto end = args.constEnd();
220 int argc = 0;
221
222 static const QRegularExpression rx(QStringLiteral("^(.*)\\b([A-Za-z_][A-Za-z0-9_]*)$"));
223
224 while (i != end) {
225 auto match = rx.match(*i);
226
227 const QString type = match.captured(1).trimmed();
228
229 if (type.isNull()) {
230 panic("Missing parameter type for argument %d of %s (%s:%d)",
231 argc, qPrintable(name), qPrintable(fileName), lineNumber);
232 }
233
234 const QString name = match.captured(2).trimmed();
235
236 if (name.isNull()) {
237 panic("Missing parameter name for argument %d of %s (%s:%d)",
238 argc, qPrintable(name), qPrintable(fileName), lineNumber);
239 }
240
241 int arrayLen = arrayLength(type);
242
243 Tracepoint::Argument a;
244 a.arrayLen = arrayLen;
245 a.name = name;
246 a.type = decayArrayToPointer(type);
247
248 t.args << std::move(a);
249
250 Tracepoint::Field f;
251 f.backendType = backendType(type);
252 f.paramType = removeBraces(type);
253 f.name = name;
254 f.arrayLen = arrayLen;
255 f.seqLen = sequenceLength(type);
256
257 t.fields << std::move(f);
258
259 ++i;
260 }
261
262 return t;
263}
264
265Provider parseProvider(const QString &filename)
266{
267 QFile f(filename);
268
269 if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
270 panic("Cannot open %s: %s", qPrintable(filename), qPrintable(f.errorString()));
271
272 QTextStream s(&f);
273
274 static const QRegularExpression tracedef(QStringLiteral("^([A-Za-z][A-Za-z0-9_]*)\\((.*)\\)$"));
275
276 Provider provider;
277 provider.name = QFileInfo(filename).baseName();
278
279 bool parsingPrefixText = false;
280 for (int lineNumber = 1; !s.atEnd(); ++lineNumber) {
281 QString line = s.readLine().trimmed();
282
283 if (line == QLatin1String("{")) {
284 parsingPrefixText = true;
285 continue;
286 } else if (parsingPrefixText && line == QLatin1String("}")) {
287 parsingPrefixText = false;
288 continue;
289 } else if (parsingPrefixText) {
290 provider.prefixText.append(line);
291 continue;
292 }
293
294 if (line.isEmpty() || line.startsWith(QLatin1Char('#')))
295 continue;
296
297 auto match = tracedef.match(line);
298 if (match.hasMatch()) {
299 const QString name = match.captured(1);
300 const QString argsString = match.captured(2);
301 const QStringList args = argsString.split(QLatin1Char(','), Qt::SkipEmptyParts);
302
303 provider.tracepoints << parseTracepoint(name, args, filename, lineNumber);
304 } else {
305 panic("Syntax error while processing '%s' line %d:\n"
306 " '%s' does not look like a tracepoint definition",
307 qPrintable(filename), lineNumber,
308 qPrintable(line));
309 }
310 }
311
312 if (parsingPrefixText) {
313 panic("Syntax error while processing '%s': "
314 "no closing brace found for prefix text block",
315 qPrintable(filename));
316 }
317
318#ifdef TRACEGEN_DEBUG
319 qDebug() << provider.prefixText;
320 for (auto i = provider.tracepoints.constBegin(); i != provider.tracepoints.constEnd(); ++i)
321 dumpTracepoint(*i);
322#endif
323
324 return provider;
325}
326