Add tracepointgen tool and convert qtgui to use it
Allows automatically generating tracepoint files by scanning source files for instrumentation macros. This makes it easier to add tracepoint support to modules and also ensures that the tracepoint files do not get out of sync with the functions they are tracing. Q_TRACE_INSTRUMENT generates entry/exit tracespoints for a function it is set. Q_TRACE_PARAM_REPLACE is used to change a function parameter for these functions to convert it to supported parameter type. Q_TRACE_POINT can be used to create a standalone tracepoint. Q_TRACE_PREFIX can be used to add prefix for generated tracing backend for example to add includes for types used in the trace points.. Task-number: QTBUG-107238 Pick-to: 6.5 Change-Id: Ib395b80838434ceb72683dac0545ca20c4d09455 Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
This commit is contained in:
parent
f488c65721
commit
9bdf74a4f2
@ -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 <private/qtrace_p.h>\n")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(qt_internal_set_compile_pdb_names target)
|
||||
if(MSVC)
|
||||
get_target_property(target_type ${target} TYPE)
|
||||
|
@ -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()
|
||||
|
@ -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 <QtCore/private/qglobal_p.h>
|
||||
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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<int>(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<double>({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();
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -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) {
|
||||
|
@ -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())
|
||||
|
22
src/tools/tracepointgen/CMakeLists.txt
Normal file
22
src/tools/tracepointgen/CMakeLists.txt
Normal file
@ -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:<TRUE>:
|
||||
# _OPTION = "host_build"
|
265
src/tools/tracepointgen/parser.cpp
Normal file
265
src/tools/tracepointgen/parser.cpp
Normal file
@ -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 <qtextstream.h>
|
||||
#include <qregularexpression.h>
|
||||
|
||||
|
||||
static void removeOffsetRange(qsizetype begin, qsizetype end, QList<LineNumber> &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<LineNumber> &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<QString> 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";
|
||||
}
|
65
src/tools/tracepointgen/parser.h
Normal file
65
src/tools/tracepointgen/parser.h
Normal file
@ -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 <qiodevice.h>
|
||||
#include <qlist.h>
|
||||
#include <qbytearray.h>
|
||||
|
||||
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<Function> m_functions;
|
||||
QList<Point> m_points;
|
||||
QList<Replace> m_replaces;
|
||||
QList<QString> m_prefixes;
|
||||
QString m_provider;
|
||||
QList<LineNumber> m_offsets;
|
||||
};
|
||||
|
||||
#endif // PARSER_H
|
64
src/tools/tracepointgen/tracepointgen.cpp
Normal file
64
src/tools/tracepointgen/tracepointgen.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include <qstring.h>
|
||||
#include <qstringlist.h>
|
||||
#include <qfile.h>
|
||||
|
||||
#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 <output file> <input files> \n");
|
||||
exit(status);
|
||||
}
|
||||
|
||||
static void parseArgs(int argc, char *argv[], QString &provider, QString &outFile, QList<QString> &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<QString> 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;
|
||||
}
|
42
src/tools/tracepointgen/tracepointgen.h
Normal file
42
src/tools/tracepointgen/tracepointgen.h
Normal file
@ -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 <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#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
|
Loading…
x
Reference in New Issue
Block a user