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 | |
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 | |
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(str: rawType.trimmed())) |
93 | return 0; |
94 | |
95 | return rx.cap(nth: 1).toInt(); |
96 | } |
97 | |
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(str: rawType.trimmed())) |
108 | return QString(); |
109 | |
110 | return rx.cap(nth: 1); |
111 | } |
112 | |
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 | |
124 | static QString removeBraces(QString type) |
125 | { |
126 | static const QRegExp rx(QStringLiteral("\\[.*\\]" )); |
127 | |
128 | return type.remove(rx); |
129 | } |
130 | |
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 | { .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 | |
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(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 | |
264 | Provider 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 | |