From 48bce2e8f0d787342f3e0f86335460fa25e8ac8f Mon Sep 17 00:00:00 2001 From: Rafael Roquetto Date: Wed, 1 Feb 2017 17:32:13 -0200 Subject: Support for LTTNG and ETW tracing This commit introduces minimal support for instrumentation within Qt. Currently, only LTTNG/Linux and ETW/Windows are supported. Change-Id: I59b48cf83acf5532a998bb493e6379e9177e14c8 Reviewed-by: Oswald Buddenhagen Reviewed-by: Shawn Rutledge Reviewed-by: Thiago Macieira --- src/tools/tracegen/provider.cpp | 304 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 src/tools/tracegen/provider.cpp (limited to 'src/tools/tracegen/provider.cpp') diff --git a/src/tools/tracegen/provider.cpp b/src/tools/tracegen/provider.cpp new file mode 100644 index 0000000000..00e105377e --- /dev/null +++ b/src/tools/tracegen/provider.cpp @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "provider.h" +#include "panic.h" + +#include +#include +#include +#include +#include + +#ifdef TRACEGEN_DEBUG +#include + +static void dumpTracepoint(const Tracepoint &t) +{ + qDebug() << "=== BEGIN TRACEPOINT ==="; + qDebug() << "name:" << t.name; + qDebug() << "ARGS\n"; + + int j = 0; + + for (auto i = t.args.constBegin(); i != t.args.constEnd(); ++i) { + qDebug() << "ARG[" << j << "] type:" << i->type; + qDebug() << "ARG[" << j << "] name:" << i->name; + qDebug() << "ARG[" << j << "] arrayLen:" << i->arrayLen; + ++j; + } + + qDebug() << "\nFIELDS\n"; + + j = 0; + + for (auto i = t.fields.constBegin(); i != t.fields.constEnd(); ++i) { + qDebug() << "FIELD[" << j << "] backend_type" << static_cast(i->backendType); + qDebug() << "FIELD[" << j << "] param_type" << i->paramType; + qDebug() << "FIELD[" << j << "] name" << i->name; + qDebug() << "FIELD[" << j << "] arrayLen" << i->arrayLen; + qDebug() << "FIELD[" << j << "] seqLen" << i->seqLen; + ++j; + } + + qDebug() << "=== END TRACEPOINT ===\n"; + +} +#endif + +static inline int arrayLength(const QString &rawType) +{ + /* matches the length of an ordinary array type + * Ex: foo[10] yields '10' + */ + static const QRegExp rx(QStringLiteral(".*\\[([0-9]+)\\].*")); + + if (!rx.exactMatch(rawType.trimmed())) + return 0; + + return rx.cap(1).toInt(); +} + +static inline QString sequenceLength(const QString &rawType) +{ + /* matches the identifier pointing to length of a CTF sequence type, which is + * a dynamic sized array. + * Ex: in qcoreapplication_foo(const char[len], some_string, unsigned int, * len) + * it will match the 'len' part of 'const char[len]') + */ + static const QRegExp rx(QStringLiteral(".*\\[([A-Za-z_][A-Za-z_0-9]*)\\].*")); + + if (!rx.exactMatch(rawType.trimmed())) + return QString(); + + return rx.cap(1); +} + +static QString decayArrayToPointer(QString type) +{ + /* decays an array to a pointer, i.e., if 'type' holds int[10], + * this function returns 'int *' + */ + static QRegExp rx(QStringLiteral("\\[(.+)\\]")); + + rx.setMinimal(true); + return type.replace(rx, QStringLiteral("*")); +} + +static QString removeBraces(QString type) +{ + static const QRegExp rx(QStringLiteral("\\[.*\\]")); + + return type.remove(rx); +} + +static Tracepoint::Field::BackendType backendType(QString rawType) +{ + static const struct { + const char *type; + Tracepoint::Field::BackendType backendType; + } typeTable[] = { + { "bool", Tracepoint::Field::Integer }, + { "short_int", Tracepoint::Field::Integer }, + { "signed_short", Tracepoint::Field::Integer }, + { "signed_short_int", Tracepoint::Field::Integer }, + { "unsigned_short", Tracepoint::Field::Integer }, + { "unsigned_short_int", Tracepoint::Field::Integer }, + { "int", Tracepoint::Field::Integer }, + { "signed", Tracepoint::Field::Integer }, + { "signed_int", Tracepoint::Field::Integer }, + { "unsigned", Tracepoint::Field::Integer }, + { "unsigned_int", Tracepoint::Field::Integer }, + { "long", Tracepoint::Field::Integer }, + { "long_int", Tracepoint::Field::Integer }, + { "signed_long", Tracepoint::Field::Integer }, + { "signed_long_int", Tracepoint::Field::Integer }, + { "unsigned_long", Tracepoint::Field::Integer }, + { "unsigned_long_int", Tracepoint::Field::Integer }, + { "long_long", Tracepoint::Field::Integer }, + { "long_long_int", Tracepoint::Field::Integer }, + { "signed_long_long", Tracepoint::Field::Integer }, + { "signed_long_long_int", Tracepoint::Field::Integer }, + { "unsigned_long_long", Tracepoint::Field::Integer }, + { "char", Tracepoint::Field::Integer }, + { "float", Tracepoint::Field::Float }, + { "double", Tracepoint::Field::Float }, + { "long_double", Tracepoint::Field::Float }, + { "char_ptr", Tracepoint::Field::String }, + { "QString", Tracepoint::Field::QtString }, + { "QByteArray", Tracepoint::Field::QtByteArray }, + { "QUrl", Tracepoint::Field::QtUrl }, + { "QRect", Tracepoint::Field::QtRect } + }; + + auto backendType = [](const QString &rawType) { + + static const size_t tableSize = sizeof (typeTable) / sizeof (typeTable[0]); + + for (size_t i = 0; i < tableSize; ++i) { + if (rawType == QLatin1String(typeTable[i].type)) + return typeTable[i].backendType; + } + + return Tracepoint::Field::Unknown; + }; + + if (arrayLength(rawType) > 0) + return Tracepoint::Field::Array; + + if (!sequenceLength(rawType).isNull()) + return Tracepoint::Field::Sequence; + + static const QRegExp constMatch(QStringLiteral("\\bconst\\b")); + rawType.remove(constMatch); + rawType.remove(QLatin1Char('&')); + + static const QRegExp ptrMatch(QStringLiteral("\\s*\\*\\s*")); + rawType.replace(ptrMatch, QStringLiteral("_ptr")); + rawType = rawType.trimmed(); + rawType.replace(QStringLiteral(" "), QStringLiteral("_")); + + return backendType(rawType.trimmed()); +} + +static Tracepoint parseTracepoint(const QString &name, const QStringList &args, + const QString &fileName, const int lineNumber) +{ + Tracepoint t; + t.name = name; + + if (args.isEmpty()) + return t; + + auto i = args.constBegin(); + auto end = args.constEnd(); + int argc = 0; + + static const QRegExp rx(QStringLiteral("(.*)\\b([A-Za-z_][A-Za-z0-9_]*)$")); + + while (i != end) { + rx.exactMatch(*i); + + const QString type = rx.cap(1).trimmed(); + + if (type.isNull()) { + panic("Missing parameter type for argument %d of %s (%s:%d)", + argc, qPrintable(name), qPrintable(fileName), lineNumber); + } + + const QString name = rx.cap(2).trimmed(); + + if (name.isNull()) { + panic("Missing parameter name for argument %d of %s (%s:%d)", + argc, qPrintable(name), qPrintable(fileName), lineNumber); + } + + int arrayLen = arrayLength(type); + + Tracepoint::Argument a; + a.arrayLen = arrayLen; + a.name = name; + a.type = decayArrayToPointer(type); + + t.args << std::move(a); + + Tracepoint::Field f; + f.backendType = backendType(type); + f.paramType = removeBraces(type); + f.name = name; + f.arrayLen = arrayLen; + f.seqLen = sequenceLength(type); + + t.fields << std::move(f); + + ++i; + } + + return t; +} + +Provider parseProvider(const QString &filename) +{ + QFile f(filename); + + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) + panic("Cannot open %s: %s", qPrintable(filename), qPrintable(f.errorString())); + + QTextStream s(&f); + + static const QRegExp tracedef(QStringLiteral("([A-Za-z][A-Za-z0-9_]*)\\((.*)\\)")); + + int lineNumber = 0; + + Provider provider; + provider.name = QFileInfo(filename).baseName(); + + for (;;) { + QString line = s.readLine().trimmed(); + + if (line.isNull()) + break; + + if (line.isEmpty() || line.startsWith(QStringLiteral("#"))) { + ++lineNumber; + continue; + } + + if (tracedef.exactMatch(line)) { + const QString name = tracedef.cap(1); + QStringList args = tracedef.cap(2).split(QStringLiteral(","), QString::SkipEmptyParts); + + if (args.at(0).isNull()) + args.clear(); + + provider.tracepoints << parseTracepoint(name, args, filename, lineNumber); + } else { + panic("Syntax error whilre processing %s on line %d", qPrintable(filename), lineNumber); + } + + ++lineNumber; + } + +#ifdef TRACEGEN_DEBUG + for (auto i = provider.tracepoints.constBegin(); i != provider.tracepoints.constEnd(); ++i) + dumpTracepoint(*i); +#endif + + return provider; +} -- cgit v1.2.3