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 | |
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 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 | |
99 | static 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 | |
115 | static 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 | |
125 | static QString removeBraces(QString type) |
126 | { |
127 | static const QRegularExpression rx(QStringLiteral("\\[.*\\]" )); |
128 | |
129 | return type.remove(rx); |
130 | } |
131 | |
132 | static 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 | |
209 | static 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 | |
265 | Provider 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 | |