diff options
Diffstat (limited to 'src/tools/tracepointgen')
-rw-r--r-- | src/tools/tracepointgen/CMakeLists.txt | 17 | ||||
-rw-r--r-- | src/tools/tracepointgen/parser.cpp | 609 | ||||
-rw-r--r-- | src/tools/tracepointgen/parser.h | 77 | ||||
-rw-r--r-- | src/tools/tracepointgen/tracepointgen.cpp | 69 | ||||
-rw-r--r-- | src/tools/tracepointgen/tracepointgen.h | 42 |
5 files changed, 814 insertions, 0 deletions
diff --git a/src/tools/tracepointgen/CMakeLists.txt b/src/tools/tracepointgen/CMakeLists.txt new file mode 100644 index 0000000000..2f473d376a --- /dev/null +++ b/src/tools/tracepointgen/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## 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 + SOURCES + tracepointgen.cpp tracepointgen.h + parser.cpp parser.h +) +qt_internal_return_unless_building_tools() diff --git a/src/tools/tracepointgen/parser.cpp b/src/tools/tracepointgen/parser.cpp new file mode 100644 index 0000000000..ad94b9a433 --- /dev/null +++ b/src/tools/tracepointgen/parser.cpp @@ -0,0 +1,609 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "tracepointgen.h" +#include "parser.h" +#include <qtextstream.h> +#include <qregularexpression.h> +#include <qfileinfo.h> + +static void removeOffsetRange(qsizetype begin, qsizetype end, QList<LineNumber> &offsets) +{ + qsizetype count = end - begin; + qsizetype i = 0; + DEBUGPRINTF2(printf("tracepointgen: removeOffsetRange: %llu %llu\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: %llu, %llu, %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 void simplifyData(QString &data) +{ + qsizetype offset = data.indexOf(QStringLiteral("//")); + while (offset >= 0) { + qsizetype endOfLine = data.indexOf(QLatin1Char('\n'), offset); + if (endOfLine == -1) + endOfLine = data.length(); + 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; + data.remove(offset, endOfComment - offset + 2); + offset = data.indexOf(QStringLiteral("/*"), offset); + } + offset = 0; + qsizetype end = 0; + while (findSpaceRange(data, offset, end)) + data.remove(offset, end - offset); +} + +static QString preprocessMetadata(const QString &in) +{ + DEBUGPRINTF(printf("in: %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("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("out: %s\n", qPrintable(out))); + return out; +} + +int Parser::lineNumber(qsizetype offset) const +{ + DEBUGPRINTF(printf("tracepointgen: lineNumber: offset %llu, line count: %llu\n", offset , m_offsets.size())); + for (const auto line : m_offsets) { + DEBUGPRINTF(printf("tracepointgen: lineNumber: %llu %llu %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 %llu", 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(preprocessMetadata(prefix)); +} + +QStringList Parser::findEnumValues(const QString &name, const QStringList &includes) +{ + QStringList split = name.split(QStringLiteral("::")); + QString enumName = split.last(); + DEBUGPRINTF(printf("searching for %s\n", qPrintable(name))); + QStringList ret; + for (const QString &filename : includes) { + QFile input(filename); + if (!input.open(QIODevice::ReadOnly | QIODevice::Text)) { + DEBUGPRINTF(printf("Cannot open '%s' for reading: %s\n", + qPrintable(filename), qPrintable(input.errorString()))); + return ret; + } + QString data; + QTextStream stream(&input); + while (!stream.atEnd()) { + QString line = stream.readLine().trimmed(); + data += line + QLatin1Char('\n'); + } + simplifyData(data); + + int pos = 0; + bool valid = true; + for (int i = 0; i < split.size() - 1; i++) { + QRegularExpression macro(QStringLiteral("(struct|class|namespace) +([A-Za-z0-9_]*)? +([A-Za-z0-9]*;?)")); + QRegularExpressionMatchIterator m = macro.globalMatch(data); + bool found = false; + while (m.hasNext() && !found) { + QRegularExpressionMatch match = m.next(); + QString n = match.captured(2); + if (!n.endsWith(QLatin1Char(';')) && n == split[i] && match.capturedStart(2) > pos) { + pos = match.capturedStart(2); + found = true; + break; + } + if (match.hasCaptured(3)) { + n = match.captured(3); + if (!n.endsWith(QLatin1Char(';')) && n == split[i] && match.capturedStart(3) > pos) { + pos = match.capturedStart(3); + found = true; + break; + } + } + } + if (!found) { + valid = false; + break; + } + } + + if (valid) { + QRegularExpression macro(QStringLiteral("enum +([A-Za-z0-9_]*)")); + QRegularExpressionMatchIterator m = macro.globalMatch(data); + while (m.hasNext()) { + QRegularExpressionMatch match = m.next(); + + if (match.capturedStart() < pos) + continue; + + QString n = match.captured(1); + + if (n == enumName) { + DEBUGPRINTF(printf("Found enum: %s\n", qPrintable(n))); + int begin = data.indexOf(QLatin1Char('{'), match.capturedEnd()); + int end = data.indexOf(QLatin1Char('}'), begin); + QString block = data.mid(begin + 1, end - begin - 1); + const QStringList enums = block.split(QLatin1Char('\n')); + for (const auto &e : enums) { + const auto trimmed = e.trimmed(); + if (!trimmed.isEmpty() && !trimmed.startsWith(QLatin1Char('#'))) + ret << trimmed; + } + + return ret; + } + } + } + } + return ret; +} + +struct EnumNameValue +{ + QString name; + QString valueStr; + int value; +}; + +static QList<EnumNameValue> enumsToValues(const QStringList &values) +{ + int cur = 0; + QList<EnumNameValue> ret; + for (const QString &value : values) { + EnumNameValue r; + if (value.contains(QLatin1Char('='))) { + size_t offset = value.indexOf(QLatin1Char('=')); + r.name = value.left(offset).trimmed(); + QString val = value.right(value.length() - offset - 1).trimmed(); + if (val.endsWith(QLatin1Char(','))) + val = val.left(val.length() - 1); + bool valid = false; + int integer = val.toInt(&valid); + if (!valid) + integer = val.toInt(&valid, 16); + if (valid) { + cur = r.value = integer; + ret << r; + } else { + auto iter = std::find_if(ret.begin(), ret.end(), [&val](const EnumNameValue &elem){ + return elem.name == val; + }); + if (iter != ret.end()) { + cur = r.value = iter->value; + ret << r; + } else { + DEBUGPRINTF(printf("Invalid value: %s %s\n", qPrintable(r.name), qPrintable(value))); + } + } + } else { + if (value.endsWith(QLatin1Char(','))) + r.name = value.left(value.length() - 1); + else + r.name = value; + r.value = ++cur; + ret << r; + } + } + return ret; +} + +void Parser::parseMetadata(const QString &data, qsizetype offset, const QStringList &includes) +{ + qsizetype beginOfProvider = data.indexOf(QLatin1Char('('), offset); + qsizetype endOfProvider = data.indexOf(QLatin1Char(','), beginOfProvider); + QString metadata; + QString provider = data.mid(beginOfProvider + 1, endOfProvider - beginOfProvider - 1).simplified(); + if (provider != m_provider) + return; + + qsizetype endOfPoint = data.indexOf(QLatin1Char(')'), endOfProvider + 1); + metadata = data.mid(endOfProvider + 1, endOfPoint - endOfProvider - 1).simplified(); + + DEBUGPRINTF(printf("tracepointgen: metadata: %s", qPrintable(metadata))); + + QString preprocessed = preprocessMetadata(metadata); + + DEBUGPRINTF2(printf("preprocessed %s\n", qPrintable(preprocessed))); + + QRegularExpression macro(QStringLiteral("([A-Z]*) ?{ ?([A-Za-z0-9=_,. ]*) ?} ?([A-Za-z0-9_:]*) ?;")); + QRegularExpressionMatchIterator i = macro.globalMatch(preprocessed); + qsizetype prev = 0; + while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + QString values = match.captured(2).trimmed(); + int cur = match.capturedStart(); + if (cur > prev) + m_metadata.append(preprocessed.mid(prev, cur - prev)); + + prev = match.capturedEnd() + 1; + DEBUGPRINTF2(printf("values: %s\n", qPrintable(values))); + if (values.isEmpty() || values.startsWith(QStringLiteral("AUTO"))) { + values.replace(QLatin1Char('\n'), QLatin1Char(' ')); + QStringList ranges; + if (values.contains(QStringLiteral("RANGE"))) { + QRegularExpression rangeMacro(QStringLiteral("RANGE +([A-Za-z0-9_]*) +... +([A-Za-z0-9_]*)")); + QRegularExpressionMatchIterator r = rangeMacro.globalMatch(values); + while (r.hasNext()) { + QRegularExpressionMatch rm = r.next(); + ranges << rm.captured(1); + ranges << rm.captured(2); + DEBUGPRINTF2(printf("range: %s ... %s\n", qPrintable(rm.captured(1)), qPrintable(rm.captured(2)))); + } + } + + const auto enumOrFlag = match.captured(1); + const auto name = match.captured(3); + const bool flags = enumOrFlag == QStringLiteral("FLAGS"); + + QStringList values = findEnumValues(name, includes); + if (values.isEmpty()) { + if (flags && name.endsWith(QLatin1Char('s'))) + values = findEnumValues(name.left(name.length() - 1), includes); + if (values.isEmpty()) { + DEBUGPRINTF(printf("Unable to find values for %s\n", qPrintable(name))); + } + } + if (!values.isEmpty()) { + auto moreValues = enumsToValues(values); + if (ranges.size()) { + for (int i = 0; i < ranges.size() / 2; i++) { + bool rangeFound = false; + for (auto &v : moreValues) { + if (v.name == ranges[2 * i]) { + rangeFound = true; + QString rangeEnd = ranges[2 * i + 1]; + auto iter = std::find_if(moreValues.begin(), moreValues.end(), [&rangeEnd](const EnumNameValue &elem){ + return elem.name == rangeEnd; + }); + if (iter != moreValues.end()) + v.valueStr = QStringLiteral("RANGE(%1, %2 ... %3)").arg(v.name).arg(v.value).arg(iter->value); + else + panic("Unable to find range end: %s\n", qPrintable(rangeEnd)); + break; + } + } + if (rangeFound == false) + panic("Unable to find range begin: %s\n", qPrintable(ranges[2 * i])); + } + } + std::sort(moreValues.begin(), moreValues.end(), [](const EnumNameValue &a, const EnumNameValue &b) { + return a.value < b.value; + }); + values.clear(); + int prevValue = std::as_const(moreValues).front().value; + for (const auto &v : std::as_const(moreValues)) { + QString a; + if (v.valueStr.isNull()) { + if (v.value == prevValue + 1 && !flags) + a = v.name; + else + a = QStringLiteral("%1 = %2").arg(v.name).arg(v.value); + prevValue = v.value; + } else { + a = v.valueStr; + } + values << a; + } + + metadata = QStringLiteral("%1 {\n %2 \n} %3;").arg(enumOrFlag).arg(values.join(QStringLiteral(",\n"))).arg(name); + if (!m_metadata.contains(metadata)) + m_metadata.append(metadata); + } + } else { + if (!m_metadata.contains(match.captured())) + m_metadata.append(match.captured()); + } + } + if (prev < preprocessed.length()) + m_metadata.append(preprocessed.mid(prev, preprocessed.length() - prev)); +} + +QString Parser::resolveInclude(const QString &filename) +{ + QFileInfo info(filename); + if (info.exists()) + return info.absoluteFilePath(); + for (const QString &sp : std::as_const(m_includeDirs)) { + info = QFileInfo(sp + QLatin1Char('/') + filename); + if (info.exists()) + return info.absoluteFilePath(); + } + return {}; +} + +void Parser::addIncludesRecursive(const QString &filename, QList<QString> &includes) +{ + QFileInfo info(filename); + DEBUGPRINTF(printf("check include: %s\n", qPrintable(filename))); + QFile input(filename); + if (!input.open(QIODevice::ReadOnly | QIODevice::Text)) { + DEBUGPRINTF(printf("Cannot open '%s' for reading: %s\n", + qPrintable(filename), qPrintable(input.errorString()))); + return; + } + QString data; + QTextStream stream(&input); + while (!stream.atEnd()) { + QString line = stream.readLine().trimmed(); + data += line + QLatin1Char(QLatin1Char('\n')); + } + + QRegularExpression includeMacro(QStringLiteral("#include [\"<]([A-Za-z0-9_./]*.h)[\">]")); + QRegularExpressionMatchIterator i = includeMacro.globalMatch(data); + while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + QString filename = match.captured(1); + + QString rinc = filename; + if (filename.startsWith(QStringLiteral("../"))) { + QFileInfo info2(info.absolutePath() + QLatin1Char('/') + filename); + if (!info2.exists()) { + DEBUGPRINTF(printf("unable to find %s\n", qPrintable(filename))); + continue; + } + rinc = info2.absoluteFilePath(); + filename = info2.fileName(); + } + + // only search possible qt headers + if (filename.startsWith(QLatin1Char('q'), Qt::CaseInsensitive)) { + QString resolved = resolveInclude(rinc); + if (!resolved.isEmpty() && !includes.contains(resolved)) { + includes.push_back(resolved); + addIncludesRecursive(resolved, includes); + } + } + } +} + +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); + + QStringList includes; + + QRegularExpression includeMacro(QStringLiteral("#include [\"<]([A-Za-z0-9_./]*.h)[\">]")); + QRegularExpressionMatchIterator i = includeMacro.globalMatch(data); + while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + const QString filename = match.captured(1); + // only search possible qt headers + if (filename.startsWith(QLatin1Char('q'), Qt::CaseInsensitive)) { + const QString resolved = resolveInclude(filename); + if (!resolved.isEmpty() && !includes.contains(resolved)) { + includes.push_back(resolved); + addIncludesRecursive(resolved, includes); + } + } + } + + QRegularExpression traceMacro(QStringLiteral("Q_TRACE_([A-Z_]*)")); + 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()); + else if (macroType == QStringLiteral("METADATA")) + parseMetadata(data, match.capturedEnd(), includes); + } + + 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 (const auto &prefix : m_prefixes) + out << prefix << "\n"; + out << QStringLiteral("}\n"); + } + for (const auto &m : m_metadata) + out << m << "\n"; + for (const auto &func : m_functions) { + out << func.className << "_" << func.functionName << "_entry(" << func.functionParameters << ")\n"; + out << func.className << "_" << func.functionName << "_exit()\n"; + } + for (const 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 0000000000..389734983d --- /dev/null +++ b/src/tools/tracepointgen/parser.h @@ -0,0 +1,77 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#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 addIncludeDirs(const QStringList &list) + { + m_includeDirs.append(list); + } + QString resolveInclude(const QString &filename); + void addIncludesRecursive(const QString &filename, QStringList &includes); + QStringList findEnumValues(const QString &name, const QStringList &includes); + + 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); + void parseMetadata(const QString &data, qsizetype offset, const QStringList &includes); + 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; + QList<QString> m_metadata; + QList<LineNumber> m_offsets; + QList<QString> m_includeDirs; + QString m_provider; +}; + +#endif // PARSER_H diff --git a/src/tools/tracepointgen/tracepointgen.cpp b/src/tools/tracepointgen/tracepointgen.cpp new file mode 100644 index 0000000000..d814c69873 --- /dev/null +++ b/src/tools/tracepointgen/tracepointgen.cpp @@ -0,0 +1,69 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#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) { + if (inputFile.startsWith(QLatin1Char('I'))) { + QStringList includeDirs = inputFile.right(inputFile.length() - 1).split(QLatin1Char(';')); + parser.addIncludeDirs(includeDirs); + continue; + } + 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 0000000000..6aed3dc574 --- /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 WITH Qt-GPL-exception-1.0 + +#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 |