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 
dumpTracepoint(const Tracepoint & t)52 static 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 
arrayLength(const QString & rawType)85 static 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(rawType.trimmed()))
93         return 0;
94 
95     return rx.cap(1).toInt();
96 }
97 
sequenceLength(const QString & rawType)98 static 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(rawType.trimmed()))
108         return QString();
109 
110     return rx.cap(1);
111 }
112 
decayArrayToPointer(QString type)113 static 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 
removeBraces(QString type)124 static QString removeBraces(QString type)
125 {
126     static const QRegExp rx(QStringLiteral("\\[.*\\]"));
127 
128     return type.remove(rx);
129 }
130 
backendType(QString rawType)131 static Tracepoint::Field::BackendType backendType(QString rawType)
132 {
133     static const struct {
134         const char *type;
135         Tracepoint::Field::BackendType backendType;
136     } typeTable[] = {
137         { "bool",                   Tracepoint::Field::Integer },
138         { "short_int",              Tracepoint::Field::Integer },
139         { "signed_short",           Tracepoint::Field::Integer },
140         { "signed_short_int",       Tracepoint::Field::Integer },
141         { "unsigned_short",         Tracepoint::Field::Integer },
142         { "unsigned_short_int",     Tracepoint::Field::Integer },
143         { "int",                    Tracepoint::Field::Integer },
144         { "signed",                 Tracepoint::Field::Integer },
145         { "signed_int",             Tracepoint::Field::Integer },
146         { "unsigned",               Tracepoint::Field::Integer },
147         { "unsigned_int",           Tracepoint::Field::Integer },
148         { "long",                   Tracepoint::Field::Integer },
149         { "long_int",               Tracepoint::Field::Integer },
150         { "signed_long",            Tracepoint::Field::Integer },
151         { "signed_long_int",        Tracepoint::Field::Integer },
152         { "unsigned_long",          Tracepoint::Field::Integer },
153         { "unsigned_long_int",      Tracepoint::Field::Integer },
154         { "long_long",              Tracepoint::Field::Integer },
155         { "long_long_int",          Tracepoint::Field::Integer },
156         { "signed_long_long",       Tracepoint::Field::Integer },
157         { "signed_long_long_int",   Tracepoint::Field::Integer },
158         { "unsigned_long_long",     Tracepoint::Field::Integer },
159         { "char",                   Tracepoint::Field::Integer },
160         { "intptr_t",               Tracepoint::Field::IntegerHex },
161         { "uintptr_t",              Tracepoint::Field::IntegerHex },
162         { "std::intptr_t",          Tracepoint::Field::IntegerHex },
163         { "std::uintptr_t",         Tracepoint::Field::IntegerHex },
164         { "float",                  Tracepoint::Field::Float },
165         { "double",                 Tracepoint::Field::Float },
166         { "long_double",            Tracepoint::Field::Float },
167         { "QString",                Tracepoint::Field::QtString },
168         { "QByteArray",             Tracepoint::Field::QtByteArray },
169         { "QUrl",                   Tracepoint::Field::QtUrl },
170         { "QRect",                  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(constMatch);
192     rawType.remove(QLatin1Char('&'));
193 
194     static const QRegExp ptrMatch(QStringLiteral("\\s*\\*\\s*"));
195     rawType.replace(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(QLatin1String("_ptr")))
203         return Tracepoint::Field::Pointer;
204 
205     return backendType(rawType);
206 }
207 
parseTracepoint(const QString & name,const QStringList & args,const QString & fileName,const int lineNumber)208 static 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(*i);
225 
226         const QString type = rx.cap(1).trimmed();
227 
228         if (type.isNull()) {
229             panic("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(2).trimmed();
234 
235         if (name.isNull()) {
236             panic("Missing parameter name for argument %d of %s (%s:%d)",
237                     argc, qPrintable(name), qPrintable(fileName), lineNumber);
238         }
239 
240         int arrayLen = arrayLength(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(type);
251         f.paramType = removeBraces(type);
252         f.name = name;
253         f.arrayLen = arrayLen;
254         f.seqLen = sequenceLength(type);
255 
256         t.fields << std::move(f);
257 
258         ++i;
259     }
260 
261     return t;
262 }
263 
parseProvider(const QString & filename)264 Provider parseProvider(const QString &filename)
265 {
266     QFile f(filename);
267 
268     if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
269         panic("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(line);
290             continue;
291         }
292 
293         if (line.isEmpty() || line.startsWith(QLatin1Char('#')))
294             continue;
295 
296         if (tracedef.exactMatch(line)) {
297             const QString name = tracedef.cap(1);
298             const QString argsString = tracedef.cap(2);
299             const QStringList args = argsString.split(QLatin1Char(','), Qt::SkipEmptyParts);
300 
301             provider.tracepoints << parseTracepoint(name, args, filename, lineNumber);
302         } else {
303             panic("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("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