diff --git a/cmake/QtTargetHelpers.cmake b/cmake/QtTargetHelpers.cmake index 6d40ec7175f..bdaeeb8b783 100644 --- a/cmake/QtTargetHelpers.cmake +++ b/cmake/QtTargetHelpers.cmake @@ -745,6 +745,75 @@ function(qt_internal_create_tracepoints name tracepoints_file) endif() endfunction() +function(qt_internal_generate_tracepoints name provider) + cmake_parse_arguments(arg "" "" "SOURCES" ${ARGN} ) + set(provider_name ${provider}) + string(PREPEND provider_name "qt") + set(tracepoint_filename "${provider_name}.tracepoints") + set(tracepoints_path "${CMAKE_CURRENT_BINARY_DIR}/${tracepoint_filename}") + set(header_filename "${provider_name}_tracepoints_p.h") + set(header_path "${CMAKE_CURRENT_BINARY_DIR}/${header_filename}") + + if(QT_FEATURE_lttng OR QT_FEATURE_etw) + + set(absolute_file_paths "") + foreach(file IN LISTS arg_SOURCES) + get_filename_component(absolute_file ${file} ABSOLUTE) + list(APPEND absolute_file_paths ${absolute_file}) + endforeach() + + if(NOT "${QT_HOST_PATH}" STREQUAL "") + qt_path_join(tracepointgen + "${QT_HOST_PATH}" + "${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_LIBEXECDIR}" + "tracepointgen") + else() + set(tracepointgen "${QT_CMAKE_EXPORT_NAMESPACE}::tracepointgen") + endif() + + add_custom_command(OUTPUT "${tracepoints_path}" + COMMAND ${tracepointgen} ${provider_name} "${tracepoints_path}" ${absolute_file_paths} + DEPENDS ${absolute_file_paths} + VERBATIM) + add_custom_target(${name}_${provider_name}_tracepoints_file DEPENDS "${tracepoints_path}") + add_dependencies(${name} ${name}_${provider_name}_tracepoints_file) + + set(source_path "${CMAKE_CURRENT_BINARY_DIR}/${provider_name}_tracepoints.cpp") + qt_configure_file(OUTPUT "${source_path}" + CONTENT "#define TRACEPOINT_CREATE_PROBES + #define TRACEPOINT_DEFINE + #include \"${header_filename}\"") + target_sources(${name} PRIVATE "${source_path}") + target_compile_definitions(${name} PRIVATE Q_TRACEPOINT) + + if(QT_FEATURE_lttng) + set(tracegen_arg "lttng") + target_link_libraries(${name} PRIVATE LTTng::UST) + elseif(QT_FEATURE_etw) + set(tracegen_arg "etw") + endif() + + if(NOT "${QT_HOST_PATH}" STREQUAL "") + qt_path_join(tracegen + "${QT_HOST_PATH}" + "${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_LIBEXECDIR}" + "tracegen") + else() + set(tracegen "${QT_CMAKE_EXPORT_NAMESPACE}::tracegen") + endif() + + get_filename_component(tracepoints_filepath "${tracepoints_path}" ABSOLUTE) + add_custom_command(OUTPUT "${header_path}" + COMMAND ${tracegen} ${tracegen_arg} "${tracepoints_filepath}" "${header_path}" + DEPENDS "${tracepoints_path}" + VERBATIM) + add_custom_target(${name}_${provider_name}_tracepoints_header DEPENDS "${header_path}") + add_dependencies(${name} ${name}_${provider_name}_tracepoints_header) + else() + qt_configure_file(OUTPUT "${header_path}" CONTENT "#include \n") + endif() +endfunction() + function(qt_internal_set_compile_pdb_names target) if(MSVC) get_target_property(target_type ${target} TYPE) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0a2c955ffe0..87232fc6643 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,6 +23,7 @@ function(find_or_build_bootstrap_names) endif() add_subdirectory(tools/moc) add_subdirectory(tools/rcc) + add_subdirectory(tools/tracepointgen) add_subdirectory(tools/tracegen) add_subdirectory(tools/cmake_automoc_parser) endfunction() diff --git a/src/corelib/global/qtrace_p.h b/src/corelib/global/qtrace_p.h index ce90a2bf186..49810737499 100644 --- a/src/corelib/global/qtrace_p.h +++ b/src/corelib/global/qtrace_p.h @@ -78,6 +78,16 @@ * * Dynamic arrays are supported using the syntax illustrated by * qcoreapplication_baz above. + * + * One can also add prefix for the generated providername_tracepoints_p.h file + * by specifying it inside brackets '{ }' in the tracepoints file. One can + * for example add forward declaration for a type: + * + * { + * QT_BEGIN_NAMESPACE + * class QEvent; + * QT_END_NAMESPACE + * } */ #include @@ -104,6 +114,72 @@ QT_BEGIN_NAMESPACE # define Q_TRACE_ENABLED(x) false #endif // defined(Q_TRACEPOINT) && !defined(QT_BOOTSTRAPPED) + +/* + * The Qt tracepoints can also be defined directly in the source files using + * the following macros. If using these macros, the tracepoints file is automatically + * generated using the tracepointgen tool. The tool scans the input files for + * these macros. These macros are ignored during compile time. Both automatic + * generation and manually specifying tracepoints in a file can't be done at the same + * time for the same provider. + * + * - Q_TRACE_INSTRUMENT(provider) + * Generate entry/exit tracepoints for a function. For example, member function + * + * void SomeClass::method(int param1, float param2) + * { + * ... + * } + * + * converted to use tracepoints: + * + * void Q_TRACE_INSTRUMENT(provider) SomeClass::method(int param1, float param2) + * { + * Q_TRACE_SCOPE(SomeClass_method, param1, param2); + * ... + * } + * + * generates following tracepoints in provider.tracepoints file: + * + * SomeClass_method_entry(int param1, float param2) + * SomeClass_method_exit() + * + * - Q_TRACE_PARAM_REPLACE(in, out) + * Can be used with Q_TRACE_INSTRUMENT to replace parameter type in with type out. + * If a parameter type is not supported by the tracegen tool, one can use this to + * change it to another supported type. + * + * void Q_TRACE_INSTRUMENT(provider) SomeClass::method(int param1, UserType param2) + * { + * Q_TRACE_PARAM_REPLACE(UserType, QString); + * Q_TRACE_SCOPE(SomeClass_method, param1, param2.toQString()); + * } + * + * - Q_TRACE_POINT(provider, tracepoint, ...) + * Manually specify tracepoint for the provider. 'tracepoint' is the full name + * of the tracepoint and ... can be zero or more parameters. + * + * Q_TRACE_POINT(provider, SomeClass_function_entry, int param1, int param2); + * + * generates following tracepoint: + * + * SomeClass_function_entry(int param1, int param2) + * + * - Q_TRACE_PREFIX(provider, prefix) + * Provide prefix for the tracepoint. Multiple prefixes can be specified for the same + * provider in different files, they are all concatenated into one in the + * provider.tracepoints file. + * + * Q_TRACE_PREFIX(provider, + * "QT_BEGIN_NAMESPACE" \ + * "class QEvent;" \ + * "QT_END_NAMESPACE") + */ +#define Q_TRACE_INSTRUMENT(provider) +#define Q_TRACE_PARAM_REPLACE(in, out) +#define Q_TRACE_POINT(provider, tracepoint, ...) +#define Q_TRACE_PREFIX(provider, prefix) + QT_END_NAMESPACE #endif // QTRACE_P_H diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 354432195a7..923d1628c16 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -973,8 +973,11 @@ qt_internal_extend_target(Gui CONDITION (QT_FEATURE_eglfs OR QT_FEATURE_xcb) util/qedidvendortable_p.h ) - -qt_internal_create_tracepoints(Gui qtgui.tracepoints) +qt_internal_generate_tracepoints(Gui gui + SOURCES + image/qimage.cpp image/qimagereader.cpp image/qpixmap.cpp kernel/qguiapplication.cpp + text/qfontdatabase.cpp +) qt_internal_add_docs(Gui doc/qtgui.qdocconf ) diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index af26d2ea4be..c9a32d89473 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -63,6 +63,15 @@ QT_WARNING_DISABLE_MSVC(4723) return QImage(); \ } +Q_TRACE_PREFIX(qtgui, + "QT_BEGIN_NAMESPACE" \ + "class QEvent;" \ + "QT_END_NAMESPACE" +) + +Q_TRACE_PARAM_REPLACE(Qt::AspectRatioMode, int); +Q_TRACE_PARAM_REPLACE(Qt::TransformationMode, int); +Q_TRACE_PARAM_REPLACE(Qt::ImageConversionFlags, int); static QImage rotated90(const QImage &src); static QImage rotated180(const QImage &src); @@ -94,11 +103,12 @@ QImageData::QImageData() Creates a new image data. Returns \nullptr if invalid parameters are give or anything else failed. */ -QImageData * QImageData::create(const QSize &size, QImage::Format format) +QImageData * Q_TRACE_INSTRUMENT(qtgui) QImageData::create(const QSize &size, QImage::Format format) { if (size.isEmpty() || format <= QImage::Format_Invalid || format >= QImage::NImageFormats) return nullptr; // invalid parameter(s) + Q_TRACE_PARAM_REPLACE(QImage::Format, int); Q_TRACE_SCOPE(QImageData_create, size, static_cast(format)); int width = size.width(); @@ -1182,7 +1192,7 @@ static void copyMetadata(QImage *dst, const QImage &src) \sa QImage() */ -QImage QImage::copy(const QRect& r) const +QImage Q_TRACE_INSTRUMENT(qtgui) QImage::copy(const QRect& r) const { Q_TRACE_SCOPE(QImage_copy, r); if (!d) @@ -2980,7 +2990,7 @@ bool QImage::isGrayscale() const \sa isNull(), {QImage#Image Transformations}{Image Transformations} */ -QImage QImage::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const +QImage Q_TRACE_INSTRUMENT(qtgui) QImage::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const { if (!d) { qWarning("QImage::scaled: Image is a null image"); @@ -3017,7 +3027,7 @@ QImage QImage::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::Transf \sa {QImage#Image Transformations}{Image Transformations} */ -QImage QImage::scaledToWidth(int w, Qt::TransformationMode mode) const +QImage Q_TRACE_INSTRUMENT(qtgui) QImage::scaledToWidth(int w, Qt::TransformationMode mode) const { if (!d) { qWarning("QImage::scaleWidth: Image is a null image"); @@ -3047,7 +3057,7 @@ QImage QImage::scaledToWidth(int w, Qt::TransformationMode mode) const \sa {QImage#Image Transformations}{Image Transformations} */ -QImage QImage::scaledToHeight(int h, Qt::TransformationMode mode) const +QImage Q_TRACE_INSTRUMENT(qtgui) QImage::scaledToHeight(int h, Qt::TransformationMode mode) const { if (!d) { qWarning("QImage::scaleHeight: Image is a null image"); @@ -3080,7 +3090,7 @@ QImage QImage::scaledToHeight(int h, Qt::TransformationMode mode) const \sa createHeuristicMask(), {QImage#Image Transformations}{Image Transformations} */ -QImage QImage::createAlphaMask(Qt::ImageConversionFlags flags) const +QImage Q_TRACE_INSTRUMENT(qtgui) QImage::createAlphaMask(Qt::ImageConversionFlags flags) const { if (!d || d->format == QImage::Format_RGB32) return QImage(); @@ -3532,7 +3542,7 @@ static inline void rgbSwapped_generic(int width, int height, const QImage *src, /*! \internal */ -QImage QImage::rgbSwapped_helper() const +QImage Q_TRACE_INSTRUMENT(qtgui) QImage::rgbSwapped_helper() const { if (isNull()) return *this; @@ -4765,12 +4775,15 @@ static QImage rotated270(const QImage &image) Transformations} */ -QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode ) const +QImage Q_TRACE_INSTRUMENT(qtgui) QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode ) const { if (!d) return QImage(); - Q_TRACE_SCOPE(QImage_transformed, matrix, mode); + Q_TRACE_PARAM_REPLACE(const QTransform &, double[9]); + Q_TRACE_SCOPE(QImage_transformed, QList({matrix.m11(), matrix.m12(), matrix.m13(), + matrix.m21(), matrix.m22(), matrix.m23(), + matrix.m31(), matrix.m32(), matrix.m33()}).data(), mode); // source image data const int ws = width(); diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index e943559ffd5..eeca0ffe7a2 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -138,6 +138,9 @@ QT_BEGIN_NAMESPACE using namespace QImageReaderWriterHelpers; using namespace Qt::StringLiterals; +Q_TRACE_POINT(qtgui, QImageReader_read_before_reading, QImageReader *reader, const QString &filename); +Q_TRACE_POINT(qtgui, QImageReader_read_after_reading, QImageReader *reader, bool result); + static QImageIOHandler *createReadHandlerHelper(QIODevice *device, const QByteArray &format, bool autoDetectImageFormat, diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 8e98a4f2545..8b7de7ac23d 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -38,6 +38,9 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; +Q_TRACE_PARAM_REPLACE(Qt::AspectRatioMode, int); +Q_TRACE_PARAM_REPLACE(Qt::TransformationMode, int); + // MSVC 19.28 does show spurious warning "C4723: potential divide by 0" for code that divides // by height() in release builds. Anyhow, all the code paths in this file are only executed // for valid QPixmap's, where height() cannot be 0. Therefore disable the warning. @@ -1032,7 +1035,7 @@ bool QPixmap::convertFromImage(const QImage &image, Qt::ImageConversionFlags fla Transformations} */ -QPixmap QPixmap::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const +QPixmap Q_TRACE_INSTRUMENT(qtgui) QPixmap::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const { if (isNull()) { qWarning("QPixmap::scaled: Pixmap is a null pixmap"); @@ -1070,7 +1073,7 @@ QPixmap QPixmap::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::Tran \sa isNull(), {QPixmap#Pixmap Transformations}{Pixmap Transformations} */ -QPixmap QPixmap::scaledToWidth(int w, Qt::TransformationMode mode) const +QPixmap Q_TRACE_INSTRUMENT(qtgui) QPixmap::scaledToWidth(int w, Qt::TransformationMode mode) const { if (isNull()) { qWarning("QPixmap::scaleWidth: Pixmap is a null pixmap"); @@ -1100,7 +1103,7 @@ QPixmap QPixmap::scaledToWidth(int w, Qt::TransformationMode mode) const \sa isNull(), {QPixmap#Pixmap Transformations}{Pixmap Transformations} */ -QPixmap QPixmap::scaledToHeight(int h, Qt::TransformationMode mode) const +QPixmap Q_TRACE_INSTRUMENT(qtgui) QPixmap::scaledToHeight(int h, Qt::TransformationMode mode) const { if (isNull()) { qWarning("QPixmap::scaleHeight: Pixmap is a null pixmap"); diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 88d3a390aa9..741a02875a3 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -1529,7 +1529,7 @@ void QGuiApplicationPrivate::eventDispatcherReady() platform_integration->initialize(); } -void QGuiApplicationPrivate::init() +void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::init() { Q_TRACE_SCOPE(QGuiApplicationPrivate_init); @@ -2016,8 +2016,9 @@ bool QGuiApplicationPrivate::processNativeEvent(QWindow *window, const QByteArra return window->nativeEvent(eventType, message, result); } -void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) +void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) { + Q_TRACE_PARAM_REPLACE(QWindowSystemInterfacePrivate::WindowSystemEvent *, int); Q_TRACE_SCOPE(QGuiApplicationPrivate_processWindowSystemEvent, e->type); switch(e->type) { diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 2e35d9dc01b..5dd3437e358 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -48,6 +48,11 @@ Q_AUTOTEST_EXPORT void qt_setQtEnableTestFont(bool value) } #endif +Q_TRACE_POINT(qtgui, QFontDatabase_loadEngine, const QString &families, int pointSize); +Q_TRACE_POINT(qtgui, QFontDatabasePrivate_addAppFont, const QString &fileName); +Q_TRACE_POINT(qtgui, QFontDatabase_addApplicationFont, const QString &fileName); +Q_TRACE_POINT(qtgui, QFontDatabase_load, const QString &family, int pointSize); + static int getFontWeight(const QString &weightString) { QString s = weightString.toLower(); @@ -780,7 +785,7 @@ QFontEngine *QFontDatabasePrivate::loadEngine(int script, const QFontDef &reques QFontEngine *engine = loadSingleEngine(script, request, family, foundry, style, size); if (engine && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol) { - Q_TRACE(QFontDatabase_loadEngine, request.families, request.pointSize); + Q_TRACE(QFontDatabase_loadEngine, request.families.join(QLatin1Char(';')), request.pointSize); QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase(); QFontEngineMulti *pfMultiEngine = pfdb->fontEngineMulti(engine, QChar::Script(script)); @@ -2522,7 +2527,7 @@ void QFontDatabasePrivate::load(const QFontPrivate *d, int script) QFontEngine *fe = nullptr; - Q_TRACE(QFontDatabase_load, req.families, req.pointSize); + Q_TRACE(QFontDatabase_load, req.families.join(QLatin1Char(';')), req.pointSize); req.fallBackFamilies = fallBackFamilies; if (!req.fallBackFamilies.isEmpty()) diff --git a/src/tools/tracepointgen/CMakeLists.txt b/src/tools/tracepointgen/CMakeLists.txt new file mode 100644 index 00000000000..17bdcc316b6 --- /dev/null +++ b/src/tools/tracepointgen/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# Generated from tracegen.pro. + +##################################################################### +## tracepointgen Tool: +##################################################################### + +qt_get_tool_target_name(target_name tracepointgen) +qt_internal_add_tool(${target_name} + CORE_LIBRARY Bootstrap + INSTALL_DIR "${INSTALL_LIBEXECDIR}" + TOOLS_TARGET Core # special case + SOURCES + tracepointgen.cpp tracepointgen.h + parser.cpp parser.h +) +qt_internal_return_unless_building_tools() + +#### Keys ignored in scope 1:.:.:tracegen.pro:: +# _OPTION = "host_build" diff --git a/src/tools/tracepointgen/parser.cpp b/src/tools/tracepointgen/parser.cpp new file mode 100644 index 00000000000..6199098371e --- /dev/null +++ b/src/tools/tracepointgen/parser.cpp @@ -0,0 +1,265 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "tracepointgen.h" +#include "parser.h" +#include +#include + + +static void removeOffsetRange(qsizetype begin, qsizetype end, QList &offsets) +{ + qsizetype count = end - begin; + qsizetype i = 0; + DEBUGPRINTF2(printf("tracepointgen: removeOffsetRange: %d %d\n", begin, end)); + while (i < offsets.size()) { + LineNumber &cur = offsets[i]; + if (begin > cur.end) { + i++; + } else if (begin >= cur.begin && begin <= cur.end) { + cur.end = begin; + i++; + } else if (begin < cur.begin && end > cur.end) { + offsets.remove(i); + DEBUGPRINTF2(printf("tracepointgen: removeOffsetRange: %d, %d, %d\n", cur.begin, cur.end, cur.line)); + } else if (end >= cur.begin && end <= cur.end) { + cur.begin = begin; + cur.end -= count; + i++; + } else if (end < cur.begin) { + cur.begin -= count; + cur.end -= count; + i++; + } + } +} + +static bool findSpaceRange(const QString &data, qsizetype &offset, qsizetype &end) { + qsizetype cur = data.indexOf(QLatin1Char(' '), offset); + if (cur >= 0) { + qsizetype i = cur + 1; + while (data.constData()[i] == QLatin1Char(' ')) i++; + if (i - cur > 1) { + offset = cur; + end = i - 1; + return true; + } + cur = data.indexOf(QLatin1Char(' '), cur + 1); + } + return false; +} + +static void simplifyData(QString &data, QList &offsets) +{ + qsizetype offset = data.indexOf(QStringLiteral("//")); + while (offset >= 0) { + qsizetype endOfLine = data.indexOf(QLatin1Char('\n'), offset); + if (endOfLine == -1) + endOfLine = data.length(); + removeOffsetRange(offset, endOfLine, offsets); + data.remove(offset, endOfLine - offset); + offset = data.indexOf(QStringLiteral("//"), offset); + } + offset = data.indexOf(QStringLiteral("/*")); + while (offset >= 0) { + qsizetype endOfComment = data.indexOf(QStringLiteral("*/"), offset); + if (endOfComment == -1) + break; + removeOffsetRange(offset, endOfComment + 2, offsets); + data.remove(offset, endOfComment - offset + 2); + offset = data.indexOf(QStringLiteral("/*"), offset); + } + offset = 0; + qsizetype end = 0; + data.replace(QLatin1Char('\n'), QLatin1Char(' ')); + while (findSpaceRange(data, offset, end)) { + removeOffsetRange(offset, end, offsets); + data.remove(offset, end - offset); + } +} + +static QString preprocessPrefix(const QString &in) +{ + DEBUGPRINTF(printf("prefix: %s\n", qPrintable(in))); + QList lines = in.split(QLatin1Char('\\')); + QString out; + for (int i = 0; i < lines.size(); i++) { + QString l = lines.at(i).simplified(); + DEBUGPRINTF(printf("prefix line: %s\n", qPrintable(l))); + if (l.length() < 2) + continue; + if (l.startsWith(QStringLiteral("\""))) + l = l.right(l.length() - 1); + if (l.endsWith(QStringLiteral("\""))) + l = l.left(l.length() - 1); + l = l.simplified(); + + if (l.length() > 1) { + if (out.size() > 0) + out.append(QLatin1Char('\n')); + out.append(l); + } + } + DEBUGPRINTF(printf("prefix out: %s\n", qPrintable(out))); + return out; +} + +int Parser::lineNumber(qsizetype offset) const +{ + DEBUGPRINTF(printf("tracepointgen: lineNumber: offset %u, line count: %u\n", offset , m_offsets.size())); + for (auto line : m_offsets) { + DEBUGPRINTF(printf("tracepointgen: lineNumber: %d %d %d\n", line.begin, line.end, line.line)); + if (offset >= line.begin && offset <= line.end) + return line.line; + } + return 0; +} + +void Parser::parseParamReplace(const QString &data, qsizetype offset, const QString &name) +{ + Replace rep; + qsizetype beginBrace = data.indexOf(QLatin1Char('('), offset); + qsizetype endBrace = data.indexOf(QLatin1Char(')'), beginBrace); + QString params = data.mid(beginBrace + 1, endBrace - beginBrace -1); + int punc = params.indexOf(QLatin1Char(',')); + if (punc < 0) + panic("Syntax error in Q_TRACE_PARAM_REPLACE at file %s, line %d", qPrintable(name), lineNumber(offset)); + rep.in = params.left(punc).simplified(); + rep.out = params.right(params.length() - punc - 1).simplified(); + if (rep.in.endsWith(QLatin1Char('*')) || rep.out.endsWith(QLatin1Char(']'))) + rep.out.append(QLatin1Char(' ')); + DEBUGPRINTF(printf("tracepointgen: replace: %s with %s\n", qPrintable(rep.in), qPrintable(rep.out))); + m_replaces.push_back(rep); +} + +void Parser::parseInstrument(const QString &data, qsizetype offset) +{ + qsizetype beginOfProvider = data.indexOf(QLatin1Char('('), offset); + qsizetype endOfProvider = data.indexOf(QLatin1Char(')'), beginOfProvider); + Function func; + QString provider = data.mid(beginOfProvider + 1, endOfProvider - beginOfProvider - 1).simplified(); + if (provider != m_provider) + return; + + qsizetype classMarker = data.indexOf(QStringLiteral("::"), endOfProvider); + qsizetype beginOfFunctionMarker = data.indexOf(QLatin1Char('{'), classMarker); + QString begin = data.mid(endOfProvider + 1, classMarker - endOfProvider - 1); + QString end = data.mid(classMarker + 2, beginOfFunctionMarker - classMarker - 2); + int spaceIndex = begin.lastIndexOf(QLatin1Char(' ')); + if (spaceIndex == -1) + func.className = begin; + else + func.className = begin.mid(spaceIndex + 1, begin.length() - spaceIndex - 1); + qsizetype braceIndex = end.indexOf(QLatin1Char('(')); + spaceIndex = end.indexOf(QLatin1Char(' ')); + if (spaceIndex < braceIndex) + func.functionName = end.left(spaceIndex).simplified(); + else + func.functionName = end.left(braceIndex).simplified(); + + qsizetype lastBraceIndex = end.lastIndexOf(QLatin1Char(')')); + func.functionParameters = end.mid(braceIndex + 1, lastBraceIndex - braceIndex - 1).simplified(); + + DEBUGPRINTF(printf("tracepointgen: %s(%s)\n", qPrintable(func.functionName), qPrintable(func.functionParameters))); + + m_functions.push_back(func); +} + +void Parser::parsePoint(const QString &data, qsizetype offset) +{ + qsizetype beginOfProvider = data.indexOf(QLatin1Char('('), offset); + qsizetype endOfProvider = data.indexOf(QLatin1Char(','), beginOfProvider); + Point point; + QString provider = data.mid(beginOfProvider + 1, endOfProvider - beginOfProvider - 1).simplified(); + if (provider != m_provider) + return; + + qsizetype endOfPoint = data.indexOf(QLatin1Char(','), endOfProvider + 1); + qsizetype endOfPoint2 = data.indexOf(QLatin1Char(')'), endOfProvider + 1); + bool params = true; + if (endOfPoint == -1 || endOfPoint2 < endOfPoint) { + endOfPoint = endOfPoint2; + params = false; + } + point.name = data.mid(endOfProvider + 1, endOfPoint - endOfProvider - 1).simplified(); + if (params) { + int endOfParams = data.indexOf(QLatin1Char(')'), endOfPoint); + point.parameters = data.mid(endOfPoint + 1, endOfParams - endOfPoint - 1).simplified(); + } + + DEBUGPRINTF(printf("tracepointgen: %s(%s)\n", qPrintable(point.name), qPrintable(point.parameters))); + + m_points.push_back(point); +} + +void Parser::parsePrefix(const QString &data, qsizetype offset) +{ + qsizetype beginOfProvider = data.indexOf(QLatin1Char('('), offset); + qsizetype endOfProvider = data.indexOf(QLatin1Char(','), beginOfProvider); + QString prefix; + QString provider = data.mid(beginOfProvider + 1, endOfProvider - beginOfProvider - 1).simplified(); + if (provider != m_provider) + return; + + qsizetype endOfPoint = data.indexOf(QLatin1Char(')'), endOfProvider + 1); + prefix = data.mid(endOfProvider + 1, endOfPoint - endOfProvider - 1).simplified(); + + DEBUGPRINTF(printf("tracepointgen: prefix: %s\n", qPrintable(prefix))); + + if (!m_prefixes.contains(prefix)) + m_prefixes.push_back(preprocessPrefix(prefix)); +} + +void Parser::parse(QIODevice &input, const QString &name) +{ + QString data; + QTextStream stream(&input); + int lineNumber = 1; + qsizetype prev = 0; + while (!stream.atEnd()) { + QString line = stream.readLine().trimmed(); + m_offsets.push_back({prev, prev + line.length(), lineNumber++}); + prev += line.length() + 1; + data += line + QLatin1Char(QLatin1Char('\n')); + } + + simplifyData(data, m_offsets); + + QRegularExpression traceMacro(QStringLiteral("Q_TRACE_([A-Z_]*)")); + QRegularExpressionMatchIterator i = traceMacro.globalMatch(data); + while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + + QString macroType = match.captured(1); + if (macroType == QStringLiteral("PARAM_REPLACE")) + parseParamReplace(data, match.capturedEnd(), name); + else if (macroType == QStringLiteral("INSTRUMENT")) + parseInstrument(data, match.capturedEnd()); + else if (macroType == QStringLiteral("POINT")) + parsePoint(data, match.capturedEnd()); + else if (macroType == QStringLiteral("PREFIX")) + parsePrefix(data, match.capturedEnd()); + } + + for (auto &func : m_functions) { + for (auto &rep : m_replaces) + func.functionParameters.replace(rep.in, rep.out); + } +} + +void Parser::write(QIODevice &input) const +{ + QTextStream out(&input); + if (m_prefixes.size() > 0) { + out << QStringLiteral("{\n"); + for (auto prefix : m_prefixes) + out << prefix << "\n"; + out << QStringLiteral("}\n"); + } + for (auto func : m_functions) { + out << func.className << "_" << func.functionName << "_entry(" << func.functionParameters << ")\n"; + out << func.className << "_" << func.functionName << "_exit()\n"; + } + for (auto point : m_points) + out << point.name << "(" << point.parameters << ")\n"; +} diff --git a/src/tools/tracepointgen/parser.h b/src/tools/tracepointgen/parser.h new file mode 100644 index 00000000000..1d1fda20682 --- /dev/null +++ b/src/tools/tracepointgen/parser.h @@ -0,0 +1,65 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef PARSER_H +#define PARSER_H + +#include +#include +#include + +struct Function +{ + QString className; + QString functionName; + QString functionParameters; +}; + +struct Point +{ + QString name; + QString parameters; +}; + +struct Replace +{ + QString in; + QString out; +}; + +struct LineNumber +{ + qsizetype begin; + qsizetype end; + int line; +}; + +struct Parser +{ + Parser(const QString &provider) + : m_provider(provider) + { + + } + void parseParamReplace(const QString &data, qsizetype offset, const QString &name); + void parseInstrument(const QString &data, qsizetype offset); + void parsePoint(const QString &data, qsizetype offset); + void parsePrefix(const QString &data, qsizetype offset); + int lineNumber(qsizetype offset) const; + + void parse(QIODevice &input, const QString &name); + void write(QIODevice &input) const; + bool isEmpty() const + { + return m_functions.isEmpty() && m_points.isEmpty(); + } + + QList m_functions; + QList m_points; + QList m_replaces; + QList m_prefixes; + QString m_provider; + QList m_offsets; +}; + +#endif // PARSER_H diff --git a/src/tools/tracepointgen/tracepointgen.cpp b/src/tools/tracepointgen/tracepointgen.cpp new file mode 100644 index 00000000000..cdf2f1fd2fd --- /dev/null +++ b/src/tools/tracepointgen/tracepointgen.cpp @@ -0,0 +1,64 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include +#include +#include + +#include "tracepointgen.h" +#include "parser.h" + +static void usage(int status) +{ + printf("Generates a tracepoint file for tracegen tool from input files.\n\n"); + printf("Usage: tracepointgen \n"); + exit(status); +} + +static void parseArgs(int argc, char *argv[], QString &provider, QString &outFile, QList &inputFiles) +{ + if (argc == 1) + usage(0); + if (argc < 4) + usage(-1); + + provider = QLatin1StringView(argv[1]); + outFile = QLatin1StringView(argv[2]); + for (int i = 3; i < argc; i++) + inputFiles.append(QLatin1StringView(argv[i])); +} + +int main(int argc, char *argv[]) +{ + QString provider; + QList inputFiles; + QString outFile; + + parseArgs(argc, argv, provider, outFile, inputFiles); + + Parser parser(provider); + + for (const QString &inputFile : inputFiles) { + QFile in(inputFile); + if (!in.open(QIODevice::ReadOnly | QIODevice::Text)) { + panic("Cannot open '%s' for reading: %s\n", + qPrintable(inputFile), qPrintable(in.errorString())); + } + DEBUGPRINTF(printf("tracepointgen: parse %s\n", qPrintable(inputFile))); + parser.parse(in, inputFile); + } + if (parser.isEmpty()) + panic("empty provider %s\n", qPrintable(provider)); + + QFile out(outFile); + + if (!out.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + panic("Cannot open '%s' for writing: %s\n", + qPrintable(outFile), qPrintable(out.errorString())); + } + + parser.write(out); + out.close(); + + return 0; +} diff --git a/src/tools/tracepointgen/tracepointgen.h b/src/tools/tracepointgen/tracepointgen.h new file mode 100644 index 00000000000..fe332ed03d5 --- /dev/null +++ b/src/tools/tracepointgen/tracepointgen.h @@ -0,0 +1,42 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef TRACEPOINTGEN_H +#define TRACEPOINTGEN_H + +#include +#include +#include + +#define DEBUG_TRACEPOINTGEN 0 + +#if DEBUG_TRACEPOINTGEN > 0 + #define DEBUGPRINTF(x) x + #if (DEBUG_TRACEPOINTGEN > 1) + #define DEBUGPRINTF2(x) x + #else + #define DEBUGPRINTF2(x) + #endif +#else + #define DEBUGPRINTF(x) + #define DEBUGPRINTF2(x) +#endif + + + +inline void panic(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "tracepointgen: fatal: "); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fputc('\n', stderr); + + exit(EXIT_FAILURE); +} + +#endif