2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2011-03-04 12:15:18 +01:00
|
|
|
|
|
|
|
|
#include "parser.h"
|
|
|
|
|
#include "announcethread.h"
|
|
|
|
|
#include "error.h"
|
|
|
|
|
#include "frame.h"
|
|
|
|
|
#include "stack.h"
|
|
|
|
|
#include "status.h"
|
|
|
|
|
#include "suppression.h"
|
2022-07-08 16:08:27 +02:00
|
|
|
#include "../valgrindtr.h"
|
|
|
|
|
|
2011-03-04 12:15:18 +01:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QAbstractSocket>
|
|
|
|
|
#include <QHash>
|
|
|
|
|
#include <QIODevice>
|
|
|
|
|
#include <QPair>
|
|
|
|
|
#include <QXmlStreamReader>
|
2011-03-04 12:15:18 +01:00
|
|
|
|
|
|
|
|
namespace {
|
2011-05-18 17:05:49 +02:00
|
|
|
|
|
|
|
|
class ParserException
|
|
|
|
|
{
|
2011-03-04 12:15:18 +01:00
|
|
|
public:
|
2011-05-18 17:05:49 +02:00
|
|
|
explicit ParserException(const QString &message)
|
|
|
|
|
: m_message(message)
|
|
|
|
|
{}
|
2011-03-04 12:15:18 +01:00
|
|
|
|
2018-12-10 08:11:18 +01:00
|
|
|
~ParserException() noexcept = default;
|
2011-03-04 12:15:18 +01:00
|
|
|
|
2011-05-18 17:05:49 +02:00
|
|
|
QString message() const { return m_message; }
|
2011-03-04 12:15:18 +01:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QString m_message;
|
|
|
|
|
};
|
|
|
|
|
|
2011-05-18 17:05:49 +02:00
|
|
|
struct XWhat
|
|
|
|
|
{
|
2011-03-04 12:15:18 +01:00
|
|
|
QString text;
|
2018-12-10 08:11:18 +01:00
|
|
|
qint64 leakedblocks = 0;
|
|
|
|
|
qint64 leakedbytes = 0;
|
|
|
|
|
qint64 hthreadid = -1;
|
2011-03-04 12:15:18 +01:00
|
|
|
};
|
|
|
|
|
|
2011-05-18 17:05:49 +02:00
|
|
|
struct XauxWhat
|
|
|
|
|
{
|
|
|
|
|
void clear() { *this = XauxWhat(); }
|
2011-03-04 12:15:18 +01:00
|
|
|
|
|
|
|
|
QString text;
|
|
|
|
|
QString file;
|
|
|
|
|
QString dir;
|
2018-12-10 08:11:18 +01:00
|
|
|
qint64 line = -1;
|
|
|
|
|
qint64 hthreadid = -1;
|
2011-03-04 12:15:18 +01:00
|
|
|
};
|
|
|
|
|
|
2011-05-18 17:05:49 +02:00
|
|
|
} // namespace anon
|
|
|
|
|
|
|
|
|
|
namespace Valgrind {
|
|
|
|
|
namespace XmlProtocol {
|
|
|
|
|
|
2023-07-25 13:08:24 +02:00
|
|
|
enum class Tool {
|
|
|
|
|
Unknown,
|
|
|
|
|
Memcheck,
|
|
|
|
|
Ptrcheck,
|
|
|
|
|
Helgrind
|
|
|
|
|
};
|
|
|
|
|
|
2011-05-18 17:05:49 +02:00
|
|
|
class Parser::Private
|
|
|
|
|
{
|
2011-03-04 12:15:18 +01:00
|
|
|
public:
|
|
|
|
|
explicit Private(Parser *qq);
|
|
|
|
|
|
|
|
|
|
void parse(QIODevice *device);
|
2011-08-05 18:03:37 +02:00
|
|
|
QString errorString;
|
2011-03-04 12:15:18 +01:00
|
|
|
|
2011-08-05 18:03:37 +02:00
|
|
|
private:
|
|
|
|
|
void parseError();
|
2011-05-18 17:05:49 +02:00
|
|
|
QVector<Frame> parseStack();
|
|
|
|
|
Suppression parseSuppression();
|
|
|
|
|
SuppressionFrame parseSuppressionFrame();
|
2011-08-05 18:03:37 +02:00
|
|
|
Frame parseFrame();
|
|
|
|
|
void parseStatus();
|
|
|
|
|
void parseErrorCounts();
|
|
|
|
|
void parseSuppressionCounts();
|
|
|
|
|
void parseAnnounceThread();
|
2011-03-04 12:15:18 +01:00
|
|
|
void checkProtocolVersion(const QString &versionStr);
|
|
|
|
|
void checkTool(const QString &tool);
|
|
|
|
|
XWhat parseXWhat();
|
|
|
|
|
XauxWhat parseXauxWhat();
|
|
|
|
|
MemcheckErrorKind parseMemcheckErrorKind(const QString &kind);
|
|
|
|
|
HelgrindErrorKind parseHelgrindErrorKind(const QString &kind);
|
|
|
|
|
PtrcheckErrorKind parsePtrcheckErrorKind(const QString &kind);
|
|
|
|
|
int parseErrorKind(const QString &kind);
|
|
|
|
|
|
|
|
|
|
void reportInternalError(const QString &errorString);
|
|
|
|
|
QXmlStreamReader::TokenType blockingReadNext();
|
|
|
|
|
bool notAtEnd() const;
|
|
|
|
|
QString blockingReadElementText();
|
|
|
|
|
|
2023-07-25 13:08:24 +02:00
|
|
|
Tool tool = Tool::Unknown;
|
2011-03-04 12:15:18 +01:00
|
|
|
QXmlStreamReader reader;
|
2011-08-05 18:03:37 +02:00
|
|
|
QHash<QString, MemcheckErrorKind> errorKindsByName_memcheck;
|
|
|
|
|
QHash<QString, HelgrindErrorKind> errorKindsByName_helgrind;
|
|
|
|
|
QHash<QString, PtrcheckErrorKind> errorKindsByName_ptrcheck;
|
2023-07-25 13:08:24 +02:00
|
|
|
QHash<QString, Tool> toolsByName;
|
2011-08-05 18:03:37 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Parser *const q;
|
2011-03-04 12:15:18 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#undef ADD_ENUM
|
2018-12-01 20:56:21 +02:00
|
|
|
#define ADD_ENUM(tool,enumV) { errorKindsByName_##tool.insert(#enumV, enumV); }
|
2011-03-04 12:15:18 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
Parser::Private::Private(Parser *qq)
|
2011-08-05 18:03:37 +02:00
|
|
|
: q(qq)
|
2011-03-04 12:15:18 +01:00
|
|
|
{
|
2023-07-25 13:08:24 +02:00
|
|
|
toolsByName.insert("memcheck", Tool::Memcheck);
|
|
|
|
|
toolsByName.insert("ptrcheck", Tool::Ptrcheck);
|
|
|
|
|
toolsByName.insert("exp-ptrcheck", Tool::Ptrcheck);
|
|
|
|
|
toolsByName.insert("helgrind", Tool::Helgrind);
|
2011-03-04 12:15:18 +01:00
|
|
|
|
|
|
|
|
ADD_ENUM(memcheck, ClientCheck)
|
|
|
|
|
ADD_ENUM(memcheck, InvalidFree)
|
|
|
|
|
ADD_ENUM(memcheck, InvalidJump)
|
|
|
|
|
ADD_ENUM(memcheck, InvalidRead)
|
|
|
|
|
ADD_ENUM(memcheck, InvalidWrite)
|
|
|
|
|
ADD_ENUM(memcheck, Leak_DefinitelyLost)
|
|
|
|
|
ADD_ENUM(memcheck, Leak_PossiblyLost)
|
|
|
|
|
ADD_ENUM(memcheck, Leak_StillReachable)
|
|
|
|
|
ADD_ENUM(memcheck, Leak_IndirectlyLost)
|
|
|
|
|
ADD_ENUM(memcheck, MismatchedFree)
|
|
|
|
|
ADD_ENUM(memcheck, Overlap)
|
|
|
|
|
ADD_ENUM(memcheck, SyscallParam)
|
|
|
|
|
ADD_ENUM(memcheck, UninitCondition)
|
|
|
|
|
ADD_ENUM(memcheck, UninitValue)
|
|
|
|
|
|
|
|
|
|
ADD_ENUM(helgrind, Race)
|
|
|
|
|
ADD_ENUM(helgrind, UnlockUnlocked)
|
|
|
|
|
ADD_ENUM(helgrind, UnlockForeign)
|
|
|
|
|
ADD_ENUM(helgrind, UnlockBogus)
|
|
|
|
|
ADD_ENUM(helgrind, PthAPIerror)
|
|
|
|
|
ADD_ENUM(helgrind, LockOrder)
|
|
|
|
|
ADD_ENUM(helgrind, Misc)
|
|
|
|
|
|
|
|
|
|
ADD_ENUM(ptrcheck, SorG)
|
|
|
|
|
ADD_ENUM(ptrcheck, Heap)
|
|
|
|
|
ADD_ENUM(ptrcheck, Arith)
|
|
|
|
|
ADD_ENUM(ptrcheck, SysParam)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef ADD_ENUM
|
|
|
|
|
|
|
|
|
|
static quint64 parseHex(const QString &str, const QString &context)
|
|
|
|
|
{
|
|
|
|
|
bool ok;
|
|
|
|
|
const quint64 v = str.toULongLong(&ok, 16);
|
|
|
|
|
if (!ok)
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("Could not parse hex number from \"%1\" (%2)").arg(str, context));
|
2011-03-04 12:15:18 +01:00
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static qint64 parseInt64(const QString &str, const QString &context)
|
|
|
|
|
{
|
|
|
|
|
bool ok;
|
|
|
|
|
const quint64 v = str.toLongLong(&ok);
|
|
|
|
|
if (!ok)
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("Could not parse hex number from \"%1\" (%2)").arg(str, context));
|
2011-03-04 12:15:18 +01:00
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QXmlStreamReader::TokenType Parser::Private::blockingReadNext()
|
|
|
|
|
{
|
|
|
|
|
QXmlStreamReader::TokenType token = QXmlStreamReader::Invalid;
|
|
|
|
|
|
|
|
|
|
forever {
|
|
|
|
|
token = reader.readNext();
|
|
|
|
|
|
|
|
|
|
if (reader.error() == QXmlStreamReader::PrematureEndOfDocumentError) {
|
|
|
|
|
if (reader.device()->waitForReadyRead(1000)) {
|
|
|
|
|
// let's try again
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
// device error, e.g. remote host closed connection, or timeout
|
|
|
|
|
// ### we have no way to know if waitForReadyRead() timed out or failed with a real
|
|
|
|
|
// error, and sensible heuristics based on QIODevice fail.
|
|
|
|
|
// - error strings are translated and in any case not guaranteed to stay the same,
|
|
|
|
|
// so we can't use them.
|
|
|
|
|
// - errorString().isEmpty() does not work because errorString() is
|
|
|
|
|
// "network timeout error" if the waitFor... timed out.
|
|
|
|
|
// - isSequential() [for socket] && isOpen() doesn't work because isOpen()
|
|
|
|
|
// returns true if the remote host closed the connection.
|
|
|
|
|
// ...so we fall back to knowing it might be a QAbstractSocket.
|
|
|
|
|
|
|
|
|
|
QIODevice *dev = reader.device();
|
2018-12-10 08:11:18 +01:00
|
|
|
auto sock = qobject_cast<const QAbstractSocket *>(dev);
|
2011-03-04 12:15:18 +01:00
|
|
|
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (!sock || sock->state() != QAbstractSocket::ConnectedState)
|
2011-03-04 12:15:18 +01:00
|
|
|
throw ParserException(dev->errorString());
|
|
|
|
|
}
|
|
|
|
|
} else if (reader.hasError()) {
|
|
|
|
|
throw ParserException(reader.errorString()); //TODO add line, column?
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
// read a valid next token
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return token;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Parser::Private::notAtEnd() const
|
|
|
|
|
{
|
|
|
|
|
return !reader.atEnd()
|
|
|
|
|
|| reader.error() == QXmlStreamReader::PrematureEndOfDocumentError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString Parser::Private::blockingReadElementText()
|
|
|
|
|
{
|
|
|
|
|
//analogous to QXmlStreamReader::readElementText(), but blocking. readElementText() doesn't recover from PrematureEndOfData,
|
|
|
|
|
//but always returns a null string if isStartElement() is false (which is the case if it already parts of the text)
|
|
|
|
|
//affects at least Qt <= 4.7.1. Reported as QTBUG-14661.
|
|
|
|
|
|
|
|
|
|
if (!reader.isStartElement())
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("trying to read element text although current position is not start of element"));
|
2011-03-04 12:15:18 +01:00
|
|
|
|
|
|
|
|
QString result;
|
|
|
|
|
|
|
|
|
|
forever {
|
|
|
|
|
const QXmlStreamReader::TokenType type = blockingReadNext();
|
|
|
|
|
switch (type) {
|
|
|
|
|
case QXmlStreamReader::Characters:
|
|
|
|
|
case QXmlStreamReader::EntityReference:
|
|
|
|
|
result += reader.text();
|
|
|
|
|
break;
|
|
|
|
|
case QXmlStreamReader::EndElement:
|
|
|
|
|
return result;
|
|
|
|
|
case QXmlStreamReader::ProcessingInstruction:
|
|
|
|
|
case QXmlStreamReader::Comment:
|
|
|
|
|
break;
|
|
|
|
|
case QXmlStreamReader::StartElement:
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("Unexpected child element while reading element text"));
|
2011-03-04 12:15:18 +01:00
|
|
|
default:
|
|
|
|
|
//TODO handle
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("Unexpected token type %1").arg(type));
|
2011-03-04 12:15:18 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return QString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Parser::Private::checkProtocolVersion(const QString &versionStr)
|
|
|
|
|
{
|
|
|
|
|
bool ok;
|
|
|
|
|
const int version = versionStr.toInt(&ok);
|
|
|
|
|
if (!ok)
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("Could not parse protocol version from \"%1\"").arg(versionStr));
|
2011-03-04 12:15:18 +01:00
|
|
|
if (version != 4)
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("XmlProtocol version %1 not supported (supported version: 4)").arg(version));
|
2011-03-04 12:15:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Parser::Private::checkTool(const QString &reportedStr)
|
|
|
|
|
{
|
2023-07-25 13:08:24 +02:00
|
|
|
const auto reported = toolsByName.constFind(reportedStr);
|
2011-03-04 12:15:18 +01:00
|
|
|
if (reported == toolsByName.constEnd())
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("Valgrind tool \"%1\" not supported").arg(reportedStr));
|
2011-03-04 12:15:18 +01:00
|
|
|
|
|
|
|
|
tool = reported.value();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XWhat Parser::Private::parseXWhat()
|
|
|
|
|
{
|
|
|
|
|
XWhat what;
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
2020-09-18 13:18:30 +02:00
|
|
|
const auto name = reader.name();
|
2020-09-28 17:29:50 +02:00
|
|
|
if (name == QLatin1String("text"))
|
2011-03-04 12:15:18 +01:00
|
|
|
what.text = blockingReadElementText();
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("leakedbytes"))
|
2018-12-01 20:56:21 +02:00
|
|
|
what.leakedbytes = parseInt64(blockingReadElementText(), "error/xwhat[memcheck]/leakedbytes");
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("leakedblocks"))
|
2018-12-01 20:56:21 +02:00
|
|
|
what.leakedblocks = parseInt64(blockingReadElementText(), "error/xwhat[memcheck]/leakedblocks");
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("hthreadid"))
|
2018-12-01 20:56:21 +02:00
|
|
|
what.hthreadid = parseInt64(blockingReadElementText(), "error/xwhat[memcheck]/hthreadid");
|
2011-03-04 12:15:18 +01:00
|
|
|
else if (reader.isStartElement())
|
|
|
|
|
reader.skipCurrentElement();
|
|
|
|
|
}
|
|
|
|
|
return what;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XauxWhat Parser::Private::parseXauxWhat()
|
|
|
|
|
{
|
|
|
|
|
XauxWhat what;
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
2020-09-18 13:18:30 +02:00
|
|
|
const auto name = reader.name();
|
2020-09-28 17:29:50 +02:00
|
|
|
if (name == QLatin1String("text"))
|
2011-03-04 12:15:18 +01:00
|
|
|
what.text = blockingReadElementText();
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("file"))
|
2011-03-04 12:15:18 +01:00
|
|
|
what.file = blockingReadElementText();
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("dir"))
|
2011-03-04 12:15:18 +01:00
|
|
|
what.dir = blockingReadElementText();
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("line"))
|
2018-12-01 20:56:21 +02:00
|
|
|
what.line = parseInt64(blockingReadElementText(), "error/xauxwhat/line");
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("hthreadid"))
|
2018-12-01 20:56:21 +02:00
|
|
|
what.hthreadid = parseInt64(blockingReadElementText(), "error/xauxwhat/hthreadid");
|
2011-03-04 12:15:18 +01:00
|
|
|
else if (reader.isStartElement())
|
|
|
|
|
reader.skipCurrentElement();
|
|
|
|
|
}
|
|
|
|
|
return what;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MemcheckErrorKind Parser::Private::parseMemcheckErrorKind(const QString &kind)
|
|
|
|
|
{
|
2023-07-25 13:08:24 +02:00
|
|
|
const auto it = errorKindsByName_memcheck.constFind(kind);
|
2011-03-04 12:15:18 +01:00
|
|
|
if (it != errorKindsByName_memcheck.constEnd())
|
|
|
|
|
return *it;
|
|
|
|
|
else
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("Unknown memcheck error kind \"%1\"").arg(kind));
|
2011-03-04 12:15:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HelgrindErrorKind Parser::Private::parseHelgrindErrorKind(const QString &kind)
|
|
|
|
|
{
|
2023-07-25 13:08:24 +02:00
|
|
|
const auto it = errorKindsByName_helgrind.constFind(kind);
|
2011-03-04 12:15:18 +01:00
|
|
|
if (it != errorKindsByName_helgrind.constEnd())
|
|
|
|
|
return *it;
|
|
|
|
|
else
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("Unknown helgrind error kind \"%1\"").arg(kind));
|
2011-03-04 12:15:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PtrcheckErrorKind Parser::Private::parsePtrcheckErrorKind(const QString &kind)
|
|
|
|
|
{
|
2023-07-25 13:08:24 +02:00
|
|
|
const auto it = errorKindsByName_ptrcheck.constFind(kind);
|
2011-03-04 12:15:18 +01:00
|
|
|
if (it != errorKindsByName_ptrcheck.constEnd())
|
|
|
|
|
return *it;
|
|
|
|
|
else
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("Unknown ptrcheck error kind \"%1\"").arg(kind));
|
2011-03-04 12:15:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Parser::Private::parseErrorKind(const QString &kind)
|
|
|
|
|
{
|
|
|
|
|
switch (tool) {
|
2023-07-25 13:08:24 +02:00
|
|
|
case Tool::Memcheck:
|
2011-03-04 12:15:18 +01:00
|
|
|
return parseMemcheckErrorKind(kind);
|
2023-07-25 13:08:24 +02:00
|
|
|
case Tool::Ptrcheck:
|
2011-03-04 12:15:18 +01:00
|
|
|
return parsePtrcheckErrorKind(kind);
|
2023-07-25 13:08:24 +02:00
|
|
|
case Tool::Helgrind:
|
2011-03-04 12:15:18 +01:00
|
|
|
return parseHelgrindErrorKind(kind);
|
2023-07-25 13:08:24 +02:00
|
|
|
case Tool::Unknown:
|
2011-03-04 12:15:18 +01:00
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("Could not parse error kind, tool not yet set."));
|
2011-03-04 12:15:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Status::State parseState(const QString &state)
|
|
|
|
|
{
|
2018-12-01 20:56:21 +02:00
|
|
|
if (state == "RUNNING")
|
2011-03-04 12:15:18 +01:00
|
|
|
return Status::Running;
|
2018-12-01 20:56:21 +02:00
|
|
|
if (state == "FINISHED")
|
2011-03-04 12:15:18 +01:00
|
|
|
return Status::Finished;
|
2022-07-08 16:08:27 +02:00
|
|
|
throw ParserException(Tr::tr("Unknown state \"%1\"").arg(state));
|
2011-03-04 12:15:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Parser::Private::reportInternalError(const QString &e)
|
|
|
|
|
{
|
|
|
|
|
errorString = e;
|
|
|
|
|
emit q->internalError(e);
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-18 17:05:49 +02:00
|
|
|
static Stack makeStack(const XauxWhat &xauxwhat, const QVector<Frame> &frames)
|
2011-03-04 12:15:18 +01:00
|
|
|
{
|
|
|
|
|
Stack s;
|
|
|
|
|
s.setFrames(frames);
|
|
|
|
|
s.setFile(xauxwhat.file);
|
|
|
|
|
s.setDirectory(xauxwhat.dir);
|
|
|
|
|
s.setLine(xauxwhat.line);
|
|
|
|
|
s.setHelgrindThreadId(xauxwhat.hthreadid);
|
|
|
|
|
s.setAuxWhat(xauxwhat.text);
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-05 18:03:37 +02:00
|
|
|
void Parser::Private::parseError()
|
2011-03-04 12:15:18 +01:00
|
|
|
{
|
|
|
|
|
Error e;
|
|
|
|
|
QVector<QVector<Frame> > frames;
|
|
|
|
|
XauxWhat currentAux;
|
|
|
|
|
QVector<XauxWhat> auxs;
|
|
|
|
|
|
|
|
|
|
int lastAuxWhat = -1;
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
|
|
|
|
if (reader.isStartElement())
|
|
|
|
|
lastAuxWhat++;
|
2020-09-18 13:18:30 +02:00
|
|
|
const auto name = reader.name();
|
2020-09-28 17:29:50 +02:00
|
|
|
if (name == QLatin1String("unique")) {
|
2018-12-01 20:56:21 +02:00
|
|
|
e.setUnique(parseHex(blockingReadElementText(), "unique"));
|
2020-09-28 17:29:50 +02:00
|
|
|
} else if (name == QLatin1String("tid")) {
|
2018-12-01 20:56:21 +02:00
|
|
|
e.setTid(parseInt64(blockingReadElementText(), "error/tid"));
|
2020-09-28 17:29:50 +02:00
|
|
|
} else if (name == QLatin1String("kind")) { //TODO this is memcheck-specific:
|
2011-03-04 12:15:18 +01:00
|
|
|
e.setKind(parseErrorKind(blockingReadElementText()));
|
2020-09-28 17:29:50 +02:00
|
|
|
} else if (name == QLatin1String("suppression")) {
|
2011-05-18 17:05:49 +02:00
|
|
|
e.setSuppression(parseSuppression());
|
2020-09-28 17:29:50 +02:00
|
|
|
} else if (name == QLatin1String("xwhat")) {
|
2011-03-04 12:15:18 +01:00
|
|
|
const XWhat xw = parseXWhat();
|
|
|
|
|
e.setWhat(xw.text);
|
|
|
|
|
e.setLeakedBlocks(xw.leakedblocks);
|
|
|
|
|
e.setLeakedBytes(xw.leakedbytes);
|
|
|
|
|
e.setHelgrindThreadId(xw.hthreadid);
|
2020-09-28 17:29:50 +02:00
|
|
|
} else if (name == QLatin1String("what")) {
|
2011-03-04 12:15:18 +01:00
|
|
|
e.setWhat(blockingReadElementText());
|
2020-09-28 17:29:50 +02:00
|
|
|
} else if (name == QLatin1String("xauxwhat")) {
|
2011-03-04 12:15:18 +01:00
|
|
|
if (!currentAux.text.isEmpty())
|
|
|
|
|
auxs.push_back(currentAux);
|
|
|
|
|
currentAux = parseXauxWhat();
|
2020-09-28 17:29:50 +02:00
|
|
|
} else if (name == QLatin1String("auxwhat")) {
|
2011-03-04 12:15:18 +01:00
|
|
|
const QString aux = blockingReadElementText();
|
|
|
|
|
//concatenate multiple consecutive <auxwhat> tags
|
|
|
|
|
if (lastAuxWhat > 1) {
|
|
|
|
|
if (!currentAux.text.isEmpty())
|
|
|
|
|
auxs.push_back(currentAux);
|
|
|
|
|
currentAux.clear();
|
|
|
|
|
currentAux.text = aux;
|
|
|
|
|
} else {
|
|
|
|
|
if (!currentAux.text.isEmpty())
|
2018-12-01 20:56:21 +02:00
|
|
|
currentAux.text.append(' ');
|
2011-03-04 12:15:18 +01:00
|
|
|
currentAux.text.append(aux);
|
|
|
|
|
}
|
|
|
|
|
lastAuxWhat = 0;
|
2020-09-28 17:29:50 +02:00
|
|
|
} else if (name == QLatin1String("stack")) {
|
2011-05-18 17:05:49 +02:00
|
|
|
frames.push_back(parseStack());
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (reader.isStartElement()) {
|
2011-03-04 12:15:18 +01:00
|
|
|
reader.skipCurrentElement();
|
2013-07-17 00:01:45 +03:00
|
|
|
}
|
2011-03-04 12:15:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!currentAux.text.isEmpty())
|
|
|
|
|
auxs.push_back(currentAux);
|
|
|
|
|
|
|
|
|
|
//if we have less xaux/auxwhats than stacks, prepend empty xauxwhats
|
|
|
|
|
//(the first frame usually has not xauxwhat in helgrind and memcheck)
|
|
|
|
|
while (auxs.size() < frames.size())
|
|
|
|
|
auxs.prepend(XauxWhat());
|
|
|
|
|
|
2017-12-13 20:09:49 +01:00
|
|
|
//add empty stacks until sizes match
|
|
|
|
|
while (frames.size() < auxs.size())
|
|
|
|
|
frames.push_back(QVector<Frame>());
|
|
|
|
|
|
2011-03-04 12:15:18 +01:00
|
|
|
QVector<Stack> stacks;
|
|
|
|
|
for (int i = 0; i < auxs.size(); ++i)
|
|
|
|
|
stacks.append(makeStack(auxs[i], frames[i]));
|
|
|
|
|
e.setStacks(stacks);
|
|
|
|
|
|
|
|
|
|
emit q->error(e);
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-05 18:03:37 +02:00
|
|
|
Frame Parser::Private::parseFrame()
|
2011-03-04 12:15:18 +01:00
|
|
|
{
|
|
|
|
|
Frame frame;
|
|
|
|
|
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
|
|
|
|
if (reader.isStartElement()) {
|
2020-09-18 13:18:30 +02:00
|
|
|
const auto name = reader.name();
|
2020-09-28 17:29:50 +02:00
|
|
|
if (name == QLatin1String("ip"))
|
2018-12-01 20:56:21 +02:00
|
|
|
frame.setInstructionPointer(parseHex(blockingReadElementText(), "error/frame/ip"));
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("obj"))
|
2011-03-04 12:15:18 +01:00
|
|
|
frame.setObject(blockingReadElementText());
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("fn"))
|
2011-03-04 12:15:18 +01:00
|
|
|
frame.setFunctionName( blockingReadElementText());
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("dir"))
|
2011-03-04 12:15:18 +01:00
|
|
|
frame.setDirectory(blockingReadElementText());
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("file"))
|
2015-06-26 14:12:33 +02:00
|
|
|
frame.setFileName(blockingReadElementText());
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("line"))
|
2018-12-01 20:56:21 +02:00
|
|
|
frame.setLine(parseInt64(blockingReadElementText(), "error/frame/line"));
|
2011-03-04 12:15:18 +01:00
|
|
|
else if (reader.isStartElement())
|
|
|
|
|
reader.skipCurrentElement();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-05 18:03:37 +02:00
|
|
|
void Parser::Private::parseAnnounceThread()
|
2011-03-04 12:15:18 +01:00
|
|
|
{
|
|
|
|
|
AnnounceThread at;
|
|
|
|
|
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
|
|
|
|
if (reader.isStartElement()) {
|
2020-09-18 13:18:30 +02:00
|
|
|
const auto name = reader.name();
|
2020-09-28 17:29:50 +02:00
|
|
|
if (name == QLatin1String("hthreadid"))
|
2018-12-01 20:56:21 +02:00
|
|
|
at.setHelgrindThreadId(parseInt64(blockingReadElementText(), "announcethread/hthreadid"));
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("stack"))
|
2011-05-18 17:05:49 +02:00
|
|
|
at.setStack(parseStack());
|
2011-03-04 12:15:18 +01:00
|
|
|
else if (reader.isStartElement())
|
|
|
|
|
reader.skipCurrentElement();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emit q->announceThread(at);
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-05 18:03:37 +02:00
|
|
|
void Parser::Private::parseErrorCounts()
|
2011-03-04 12:15:18 +01:00
|
|
|
{
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
|
|
|
|
if (reader.isStartElement()) {
|
2020-09-28 17:29:50 +02:00
|
|
|
if (reader.name() == QLatin1String("pair")) {
|
2011-03-04 12:15:18 +01:00
|
|
|
qint64 unique = 0;
|
|
|
|
|
qint64 count = 0;
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
|
|
|
|
if (reader.isStartElement()) {
|
2020-09-18 13:18:30 +02:00
|
|
|
const auto name = reader.name();
|
2020-09-28 17:29:50 +02:00
|
|
|
if (name == QLatin1String("unique"))
|
2018-12-01 20:56:21 +02:00
|
|
|
unique = parseHex(blockingReadElementText(), "errorcounts/pair/unique");
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("count"))
|
2018-12-01 20:56:21 +02:00
|
|
|
count = parseInt64(blockingReadElementText(), "errorcounts/pair/count");
|
2011-03-04 12:15:18 +01:00
|
|
|
else if (reader.isStartElement())
|
|
|
|
|
reader.skipCurrentElement();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
emit q->errorCount(unique, count);
|
2020-09-28 17:29:50 +02:00
|
|
|
} else if (reader.isStartElement())
|
2011-03-04 12:15:18 +01:00
|
|
|
reader.skipCurrentElement();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-08-05 18:03:37 +02:00
|
|
|
void Parser::Private::parseSuppressionCounts()
|
2011-03-04 12:15:18 +01:00
|
|
|
{
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
|
|
|
|
if (reader.isStartElement()) {
|
2020-09-28 17:29:50 +02:00
|
|
|
if (reader.name() == QLatin1String("pair")) {
|
2011-08-05 18:03:37 +02:00
|
|
|
QString pairName;
|
2011-03-04 12:15:18 +01:00
|
|
|
qint64 count = 0;
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
|
|
|
|
if (reader.isStartElement()) {
|
2020-09-18 13:18:30 +02:00
|
|
|
const auto name = reader.name();
|
2020-09-28 17:29:50 +02:00
|
|
|
if (name == QLatin1String("name"))
|
2011-08-05 18:03:37 +02:00
|
|
|
pairName = blockingReadElementText();
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("count"))
|
2018-12-01 20:56:21 +02:00
|
|
|
count = parseInt64(blockingReadElementText(), "suppcounts/pair/count");
|
2011-03-04 12:15:18 +01:00
|
|
|
else if (reader.isStartElement())
|
|
|
|
|
reader.skipCurrentElement();
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-05 18:03:37 +02:00
|
|
|
emit q->suppressionCount(pairName, count);
|
2020-09-28 17:29:50 +02:00
|
|
|
} else if (reader.isStartElement())
|
2011-03-04 12:15:18 +01:00
|
|
|
reader.skipCurrentElement();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-05 18:03:37 +02:00
|
|
|
void Parser::Private::parseStatus()
|
2011-03-04 12:15:18 +01:00
|
|
|
{
|
|
|
|
|
Status s;
|
|
|
|
|
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
|
|
|
|
if (reader.isStartElement()) {
|
2020-09-18 13:18:30 +02:00
|
|
|
const auto name = reader.name();
|
2020-09-28 17:29:50 +02:00
|
|
|
if (name == QLatin1String("state"))
|
2011-03-04 12:15:18 +01:00
|
|
|
s.setState(parseState(blockingReadElementText()));
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("time"))
|
2011-03-04 12:15:18 +01:00
|
|
|
s.setTime(blockingReadElementText());
|
|
|
|
|
else if (reader.isStartElement())
|
|
|
|
|
reader.skipCurrentElement();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emit q->status(s);
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-18 17:05:49 +02:00
|
|
|
QVector<Frame> Parser::Private::parseStack()
|
2011-03-04 12:15:18 +01:00
|
|
|
{
|
|
|
|
|
QVector<Frame> frames;
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
|
|
|
|
if (reader.isStartElement()) {
|
2020-09-28 17:29:50 +02:00
|
|
|
if (reader.name() == QLatin1String("frame"))
|
2011-08-05 18:03:37 +02:00
|
|
|
frames.append(parseFrame());
|
2011-03-04 12:15:18 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return frames;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-18 17:05:49 +02:00
|
|
|
SuppressionFrame Parser::Private::parseSuppressionFrame()
|
2011-03-04 12:15:18 +01:00
|
|
|
{
|
|
|
|
|
SuppressionFrame frame;
|
|
|
|
|
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
|
|
|
|
if (reader.isStartElement()) {
|
2020-09-18 13:18:30 +02:00
|
|
|
const auto name = reader.name();
|
2020-09-28 17:29:50 +02:00
|
|
|
if (name == QLatin1String("obj"))
|
2011-03-04 12:15:18 +01:00
|
|
|
frame.setObject(blockingReadElementText());
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("fun"))
|
2011-03-04 12:15:18 +01:00
|
|
|
frame.setFunction( blockingReadElementText());
|
|
|
|
|
else if (reader.isStartElement())
|
|
|
|
|
reader.skipCurrentElement();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-18 17:05:49 +02:00
|
|
|
Suppression Parser::Private::parseSuppression()
|
2011-03-04 12:15:18 +01:00
|
|
|
{
|
|
|
|
|
Suppression supp;
|
2011-05-18 17:05:49 +02:00
|
|
|
SuppressionFrames frames;
|
2011-03-04 12:15:18 +01:00
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
|
|
|
|
if (reader.isEndElement())
|
|
|
|
|
break;
|
|
|
|
|
if (reader.isStartElement()) {
|
2020-09-18 13:18:30 +02:00
|
|
|
const auto name = reader.name();
|
2020-09-28 17:29:50 +02:00
|
|
|
if (name == QLatin1String("sname"))
|
2011-03-04 12:15:18 +01:00
|
|
|
supp.setName(blockingReadElementText());
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("skind"))
|
2011-03-04 12:15:18 +01:00
|
|
|
supp.setKind(blockingReadElementText());
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("skaux"))
|
2011-03-04 12:15:18 +01:00
|
|
|
supp.setAuxKind(blockingReadElementText());
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("rawtext"))
|
2011-03-04 12:15:18 +01:00
|
|
|
supp.setRawText(blockingReadElementText());
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("sframe"))
|
2011-05-18 17:05:49 +02:00
|
|
|
frames.push_back(parseSuppressionFrame());
|
2011-03-04 12:15:18 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
supp.setFrames(frames);
|
|
|
|
|
return supp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Parser::Private::parse(QIODevice *device)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(device, return);
|
|
|
|
|
reader.setDevice(device);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
while (notAtEnd()) {
|
|
|
|
|
blockingReadNext();
|
2020-09-18 13:18:30 +02:00
|
|
|
const auto name = reader.name();
|
2020-09-28 17:29:50 +02:00
|
|
|
if (name == QLatin1String("error"))
|
2011-08-05 18:03:37 +02:00
|
|
|
parseError();
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("announcethread"))
|
2011-08-05 18:03:37 +02:00
|
|
|
parseAnnounceThread();
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("status"))
|
2011-08-05 18:03:37 +02:00
|
|
|
parseStatus();
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("errorcounts"))
|
2011-08-05 18:03:37 +02:00
|
|
|
parseErrorCounts();
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("suppcounts"))
|
2011-08-05 18:03:37 +02:00
|
|
|
parseSuppressionCounts();
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("protocolversion"))
|
2011-03-04 12:15:18 +01:00
|
|
|
checkProtocolVersion(blockingReadElementText());
|
2020-09-28 17:29:50 +02:00
|
|
|
else if (name == QLatin1String("protocoltool"))
|
2011-03-04 12:15:18 +01:00
|
|
|
checkTool(blockingReadElementText());
|
|
|
|
|
}
|
|
|
|
|
} catch (const ParserException &e) {
|
|
|
|
|
reportInternalError(e.message());
|
|
|
|
|
} catch (...) {
|
2022-07-08 16:08:27 +02:00
|
|
|
reportInternalError(Tr::tr("Unexpected exception caught during parsing."));
|
2011-03-04 12:15:18 +01:00
|
|
|
}
|
|
|
|
|
emit q->finished();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Parser::Parser(QObject *parent)
|
|
|
|
|
: QObject(parent)
|
|
|
|
|
, d(new Private(this))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Parser::~Parser()
|
|
|
|
|
{
|
|
|
|
|
delete d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString Parser::errorString() const
|
|
|
|
|
{
|
|
|
|
|
return d->errorString;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Parser::parse(QIODevice *device)
|
|
|
|
|
{
|
|
|
|
|
d->parse(device);
|
|
|
|
|
}
|
2011-05-18 17:05:49 +02:00
|
|
|
|
|
|
|
|
} // namespace XmlParser
|
|
|
|
|
} // namespace Valgrind
|