-
Notifications
You must be signed in to change notification settings - Fork 257
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
227 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
#include "tracepointformat.h" | ||
|
||
TracePointFormatter::TracePointFormatter(const QString& format) | ||
{ | ||
// ignore empty format strings | ||
if (format.isEmpty()) { | ||
return; | ||
} | ||
|
||
// the format string are the arguments to a printf call, therefor the format will always be in quotes and then | ||
// follows a list of arguments | ||
auto endOfFormatString = format.indexOf(QLatin1Char('\"'), 1); | ||
|
||
auto lastRec = endOfFormatString; | ||
auto recIndex = lastRec; | ||
|
||
// no quote in format string -> format string is not a string | ||
if (endOfFormatString == -1) { | ||
return; | ||
} | ||
|
||
// check for valid format string | ||
// some format strings contains this tries to filter these out | ||
for (int i = endOfFormatString; i < format.size(); i++) { | ||
auto c = format[i]; | ||
auto nextC = i < format.size() - 1 ? format[i + 1] : QChar {}; | ||
|
||
if ((c == QLatin1Char('>') && nextC == QLatin1Char('>')) | ||
|| (c == QLatin1Char('<') && nextC == QLatin1Char('<'))) { | ||
return; | ||
} | ||
} | ||
|
||
// set format string after validating we can print it | ||
m_formatString = format.mid(1, endOfFormatString - 1); | ||
|
||
while ((recIndex = format.indexOf(QStringLiteral("REC->"), lastRec)) != -1) { | ||
auto endOfName = format.indexOf(QLatin1Char(')'), recIndex); | ||
|
||
auto start = recIndex + 5; // 5 because we want the field after REC-> | ||
m_args.push_back(format.mid(start, endOfName - start)); | ||
lastRec = recIndex + 1; | ||
} | ||
} | ||
|
||
QString TracePointFormatter::format(const Data::TracePointData& data) const | ||
{ | ||
QString result; | ||
|
||
// if m_formatString is empty, we couldn't parse it, just dump out the information | ||
if (m_formatString.isEmpty()) { | ||
for (auto it = data.cbegin(), end = data.cend(); it != end; it++) { | ||
result += QLatin1String("%1: %2\n").arg(it.key(), QString::number(it->toULongLong())); | ||
} | ||
return result.trimmed(); | ||
} | ||
|
||
const auto percent = QLatin1Char('%'); | ||
auto currentPercent = 0; | ||
|
||
for (int i = 0; i < m_args.size();) { | ||
auto nextPercent = m_formatString.indexOf(percent, currentPercent + 1); | ||
|
||
auto substring = m_formatString.mid(currentPercent, nextPercent - currentPercent); | ||
if (substring.contains(percent)) { | ||
result += QString::asprintf(qPrintable(substring), data.value(m_args[i]).toULongLong()); | ||
i++; | ||
|
||
currentPercent = nextPercent; | ||
} else { | ||
result += substring; | ||
} | ||
currentPercent = nextPercent; | ||
} | ||
|
||
return result; | ||
} | ||
|
||
QString formatTracepoint(const Data::TracePointFormat& format, const Data::TracePointData& data) | ||
{ | ||
TracePointFormatter formatter(format.format); | ||
return QStringLiteral("%1:%2:\n%3").arg(format.systemId, format.nameId, formatter.format(data)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
SPDX-FileCopyrightText: Lieven Hey <[email protected]> | ||
SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, [email protected] | ||
SPDX-License-Identifier: GPL-2.0-or-later | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <QString> | ||
|
||
#include "data.h" | ||
|
||
class TracePointFormatter | ||
{ | ||
public: | ||
TracePointFormatter(const QString& format); | ||
|
||
QString format(const Data::TracePointData& data) const; | ||
|
||
QString formatString() const | ||
{ | ||
return m_formatString; | ||
} | ||
QStringList args() const | ||
{ | ||
return m_args; | ||
} | ||
|
||
private: | ||
QString m_formatString; | ||
QStringList m_args; | ||
}; | ||
|
||
QString formatTracepoint(const Data::TracePointFormat& format, const Data::TracePointData& data); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/* | ||
SPDX-FileCopyrightText: Lieven Hey <[email protected]> | ||
SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, [email protected] | ||
SPDX-License-Identifier: GPL-2.0-or-later | ||
*/ | ||
|
||
#include <QTest> | ||
|
||
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) | ||
#include <QHashSeed> | ||
#endif // QT_VERSION < QT_VERSION_CHECK(6, 2, 0) | ||
|
||
#include <tracepointformat.h> | ||
|
||
class TestTracepointFormat : public QObject | ||
{ | ||
Q_OBJECT | ||
private slots: | ||
void initTestCase() | ||
{ | ||
#if QT_VERSION < QT_VERSION_CHECK(6, 2, 0) | ||
qSetGlobalQHashSeed(0); | ||
#else | ||
QHashSeed::setDeterministicGlobalSeed(); | ||
#endif // QT_VERSION < QT_VERSION_CHECK(6, 2, 0) | ||
} | ||
|
||
void testFormatString() | ||
{ | ||
// taken from /sys/kernel/tracing/events/syscalls/sys_enter_openat/format | ||
auto format = QStringLiteral( | ||
"\"dfd: 0x%08lx, filename: 0x%08lx, flags: 0x%08lx, mode: 0x%08lx\", ((unsigned long)(REC->dfd)), " | ||
"((unsigned long)(REC->filename)), ((unsigned long)(REC->flags)), ((unsigned long)(REC->mode))"); | ||
|
||
TracePointFormatter formatter(format); | ||
|
||
QCOMPARE(formatter.formatString(), | ||
QStringLiteral("dfd: 0x%08lx, filename: 0x%08lx, flags: 0x%08lx, mode: 0x%08lx")); | ||
QCOMPARE(formatter.args(), | ||
(QStringList {{QStringLiteral("dfd")}, | ||
{QStringLiteral("filename")}, | ||
{QStringLiteral("flags")}, | ||
{QStringLiteral("mode")}})); | ||
} | ||
|
||
void testSyscallEnterOpenat() | ||
{ | ||
Data::TracePointData tracepointData = {{QStringLiteral("filename"), QVariant(140732347873408ull)}, | ||
{QStringLiteral("dfd"), QVariant(4294967196ull)}, | ||
{QStringLiteral("__syscall_nr"), QVariant(257)}, | ||
{QStringLiteral("flags"), QVariant(0ull)}, | ||
{QStringLiteral("mode"), QVariant(0)}}; | ||
|
||
const Data::TracePointFormat format = { | ||
QStringLiteral("syscalls"), QStringLiteral("syscall_enter_openat"), 0, | ||
QStringLiteral( | ||
"\"dfd: 0x%08lx, filename: 0x%08lx, flags: 0x%08lx, mode: 0x%08lx\", ((unsigned long)(REC->dfd)), " | ||
"((unsigned long)(REC->filename)), ((unsigned long)(REC->flags)), ((unsigned long)(REC->mode))")}; | ||
|
||
TracePointFormatter formatter(format.format); | ||
} | ||
|
||
void testInvalidFormatString_data() | ||
{ | ||
QTest::addColumn<QString>("format"); | ||
QTest::addRow("Too complex format") << QStringLiteral( | ||
"\"%d,%d %s (%s) %llu + %u %s,%u,%u [%d]\", ((unsigned int) ((REC->dev) >> 20)), ((unsigned int) " | ||
"((REC->dev) & ((1U << 20) - 1))), REC->rwbs, __get_str(cmd), (unsigned long long)REC->sector, " | ||
"REC->nr_sector, __print_symbolic((((REC->ioprio) >> 13) & (8 - 1)), { IOPRIO_CLASS_NONE, \"none\" }, " | ||
"{IOPRIO_CLASS_RT, \"rt\"}, {IOPRIO_CLASS_BE, \"be\"}, {IOPRIO_CLASS_IDLE, \"idle\"}, " | ||
"{IOPRIO_CLASS_INVALID, \"invalid\"}), (((REC->ioprio) >> 3) & ((1 << 10) - 1)), ((REC->ioprio) & ((1 << " | ||
"3) - 1)), REC->error "); | ||
|
||
QTest::addRow("Invalid format string") << QStringLiteral("abc123%s"); | ||
QTest::addRow("Emptry format string") << QString {}; | ||
} | ||
void testInvalidFormatString() | ||
{ | ||
QFETCH(QString, format); | ||
|
||
Data::TracePointData data = {{QStringLiteral("ioprio"), QVariant(0)}, | ||
{QStringLiteral("sector"), QVariant(18446744073709551615ull)}, | ||
{QStringLiteral("nr_sector"), QVariant(0u)}, | ||
{QStringLiteral("rwbs"), QVariant(QByteArray("N\x00\x00\x00\x00\x00\x00\x00"))}, | ||
{QStringLiteral("dev"), QVariant(8388624u)}, | ||
{QStringLiteral("cmd"), QVariant(65584u)}, | ||
{QStringLiteral("error"), QVariant(-5)}}; | ||
|
||
TracePointFormatter formatter(format); | ||
QVERIFY(formatter.formatString().isEmpty()); | ||
|
||
// if the format string cannot be decoded then for formatter will just concat the tracepoint data | ||
// Qt5 and Qt6 use different hashing functions so we need two different outputs | ||
#if QT_VERSION < QT_VERSION_CHECK(6, 2, 0) | ||
auto output = QLatin1String("dev: 8388624\ncmd: 65584\nnr_sector: 0\nrwbs: 0\nioprio: 0\nerror: " | ||
"18446744073709551611\nsector: 18446744073709551615"); | ||
#else | ||
auto output = QLatin1String("cmd: 65584\nioprio: 0\nnr_sector: 0\nrwbs: 0\nsector: 18446744073709551615\ndev: " | ||
"8388624\nerror: 18446744073709551611"); | ||
#endif // QT_VERSION < QT_VERSION_CHECK(6, 2, 0) | ||
|
||
QCOMPARE(formatter.format(data), output); | ||
} | ||
}; | ||
|
||
QTEST_GUILESS_MAIN(TestTracepointFormat) | ||
|
||
#include "tst_tracepointformat.moc" |