2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2011-02-18 10:36:52 +01:00
|
|
|
**
|
2015-01-14 18:07:15 +01:00
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
|
|
|
** Contact: http://www.qt.io/licensing
|
2011-02-18 10:36:52 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2011-02-18 10:36:52 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2015-01-14 18:07:15 +01:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms and
|
|
|
|
|
** conditions see http://www.qt.io/terms-conditions. For further information
|
2014-10-01 13:21:18 +02:00
|
|
|
** use the contact form at http://www.qt.io/contact-us.
|
2011-02-18 10:36:52 +01:00
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
2012-10-02 09:12:39 +02:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
2014-10-01 13:21:18 +02:00
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
2012-10-02 09:12:39 +02:00
|
|
|
**
|
2015-01-14 18:07:15 +01:00
|
|
|
** In addition, as a special exception, The Qt Company gives you certain additional
|
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
2011-02-18 10:36:52 +01:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2011-02-18 10:36:52 +01:00
|
|
|
|
2013-01-24 16:33:17 +01:00
|
|
|
// NOTE: Don't add dependencies to other files.
|
|
|
|
|
// This is used in the debugger auto-tests.
|
|
|
|
|
|
2010-04-26 18:54:08 +02:00
|
|
|
#include "watchdata.h"
|
2012-08-21 16:31:03 +02:00
|
|
|
#include "watchutils.h"
|
2013-02-01 13:05:28 +01:00
|
|
|
#include "debuggerprotocol.h"
|
2010-04-26 18:54:08 +02:00
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QDebug>
|
2010-04-26 18:54:08 +02:00
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// WatchData
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
namespace Debugger {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2010-10-15 13:00:14 +02:00
|
|
|
bool isPointerType(const QByteArray &type)
|
|
|
|
|
{
|
|
|
|
|
return type.endsWith('*') || type.endsWith("* const");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isCharPointerType(const QByteArray &type)
|
|
|
|
|
{
|
|
|
|
|
return type == "char *" || type == "const char *" || type == "char const *";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isIntType(const QByteArray &type)
|
|
|
|
|
{
|
|
|
|
|
if (type.isEmpty())
|
|
|
|
|
return false;
|
|
|
|
|
switch (type.at(0)) {
|
|
|
|
|
case 'b':
|
|
|
|
|
return type == "bool";
|
|
|
|
|
case 'c':
|
|
|
|
|
return type == "char";
|
|
|
|
|
case 'i':
|
2012-03-13 10:38:44 +01:00
|
|
|
return type == "int";
|
2010-10-15 13:00:14 +02:00
|
|
|
case 'l':
|
|
|
|
|
return type == "long"
|
2012-03-13 10:38:44 +01:00
|
|
|
|| type == "long int"
|
|
|
|
|
|| type == "long unsigned int";
|
2010-10-15 13:00:14 +02:00
|
|
|
case 'p':
|
|
|
|
|
return type == "ptrdiff_t";
|
|
|
|
|
case 'q':
|
|
|
|
|
return type == "qint16" || type == "quint16"
|
|
|
|
|
|| type == "qint32" || type == "quint32"
|
2011-03-23 13:06:16 +01:00
|
|
|
|| type == "qint64" || type == "quint64"
|
|
|
|
|
|| type == "qlonglong" || type == "qulonglong";
|
2010-10-15 13:00:14 +02:00
|
|
|
case 's':
|
|
|
|
|
return type == "short"
|
|
|
|
|
|| type == "signed"
|
|
|
|
|
|| type == "size_t"
|
|
|
|
|
|| type == "std::size_t"
|
|
|
|
|
|| type == "std::ptrdiff_t"
|
2012-03-13 10:38:44 +01:00
|
|
|
|| (type.startsWith("signed") &&
|
|
|
|
|
( type == "signed char"
|
|
|
|
|
|| type == "signed short"
|
|
|
|
|
|| type == "signed short int"
|
|
|
|
|
|| type == "signed long"
|
|
|
|
|
|| type == "signed long int"
|
|
|
|
|
|| type == "signed long long"
|
|
|
|
|
|| type == "signed long long int"));
|
2010-10-15 13:00:14 +02:00
|
|
|
case 'u':
|
|
|
|
|
return type == "unsigned"
|
2012-03-13 10:38:44 +01:00
|
|
|
|| (type.startsWith("unsigned") &&
|
|
|
|
|
( type == "unsigned char"
|
|
|
|
|
|| type == "unsigned short"
|
|
|
|
|
|| type == "unsigned short int"
|
2013-09-03 13:01:52 +02:00
|
|
|
|| type == "unsigned int"
|
2012-03-13 10:38:44 +01:00
|
|
|
|| type == "unsigned long"
|
|
|
|
|
|| type == "unsigned long int"
|
|
|
|
|
|| type == "unsigned long long"
|
|
|
|
|
|| type == "unsigned long long int"));
|
2010-10-15 13:00:14 +02:00
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isFloatType(const QByteArray &type)
|
|
|
|
|
{
|
2013-05-05 22:43:52 +03:00
|
|
|
return type == "float" || type == "double" || type == "qreal";
|
2010-10-15 13:00:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isIntOrFloatType(const QByteArray &type)
|
|
|
|
|
{
|
|
|
|
|
return isIntType(type) || isFloatType(type);
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-26 18:54:08 +02:00
|
|
|
WatchData::WatchData() :
|
2010-10-15 13:00:14 +02:00
|
|
|
id(0),
|
|
|
|
|
state(InitialState),
|
2010-04-26 18:54:08 +02:00
|
|
|
editformat(0),
|
2010-09-22 11:19:35 +02:00
|
|
|
address(0),
|
2013-03-06 10:03:29 +01:00
|
|
|
origaddr(0),
|
2011-04-04 16:15:03 +02:00
|
|
|
size(0),
|
2011-03-01 17:04:36 +01:00
|
|
|
bitpos(0),
|
|
|
|
|
bitsize(0),
|
2014-05-16 00:18:17 +02:00
|
|
|
elided(0),
|
2010-10-15 13:00:14 +02:00
|
|
|
hasChildren(false),
|
2010-04-26 18:54:08 +02:00
|
|
|
valueEnabled(true),
|
|
|
|
|
valueEditable(true),
|
|
|
|
|
error(false),
|
2010-10-15 13:00:14 +02:00
|
|
|
sortId(0),
|
|
|
|
|
source(0)
|
2010-04-26 18:54:08 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool WatchData::isEqual(const WatchData &other) const
|
|
|
|
|
{
|
|
|
|
|
return iname == other.iname
|
|
|
|
|
&& exp == other.exp
|
|
|
|
|
&& name == other.name
|
|
|
|
|
&& value == other.value
|
|
|
|
|
&& editvalue == other.editvalue
|
|
|
|
|
&& type == other.type
|
|
|
|
|
&& displayedType == other.displayedType
|
|
|
|
|
&& variable == other.variable
|
2010-09-22 11:19:35 +02:00
|
|
|
&& address == other.address
|
2011-04-04 16:15:03 +02:00
|
|
|
&& size == other.size
|
2014-05-16 00:18:17 +02:00
|
|
|
&& elided == other.elided
|
2010-04-26 18:54:08 +02:00
|
|
|
&& hasChildren == other.hasChildren
|
|
|
|
|
&& valueEnabled == other.valueEnabled
|
|
|
|
|
&& valueEditable == other.valueEditable
|
|
|
|
|
&& error == other.error;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 09:58:23 +01:00
|
|
|
bool WatchData::isAncestorOf(const QByteArray &childIName) const
|
|
|
|
|
{
|
|
|
|
|
if (iname.size() >= childIName.size())
|
|
|
|
|
return false;
|
|
|
|
|
if (!childIName.startsWith(iname))
|
|
|
|
|
return false;
|
|
|
|
|
return childIName.at(iname.size()) == '.';
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-11 17:51:29 +01:00
|
|
|
bool WatchData::isVTablePointer() const
|
|
|
|
|
{
|
|
|
|
|
// First case: Cdb only. No user type can be named like this, this is safe.
|
|
|
|
|
// Second case: Python dumper only.
|
|
|
|
|
return type.startsWith("__fptr()")
|
|
|
|
|
|| (type.isEmpty() && name == QLatin1String("[vptr]"));
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-26 18:54:08 +02:00
|
|
|
void WatchData::setError(const QString &msg)
|
|
|
|
|
{
|
|
|
|
|
setAllUnneeded();
|
|
|
|
|
value = msg;
|
|
|
|
|
setHasChildren(false);
|
|
|
|
|
valueEnabled = false;
|
|
|
|
|
valueEditable = false;
|
|
|
|
|
error = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchData::setValue(const QString &value0)
|
|
|
|
|
{
|
|
|
|
|
value = value0;
|
2011-12-21 14:02:52 +01:00
|
|
|
if (value == QLatin1String("{...}")) {
|
2010-04-26 18:54:08 +02:00
|
|
|
value.clear();
|
|
|
|
|
hasChildren = true; // at least one...
|
|
|
|
|
}
|
|
|
|
|
// strip off quoted characters for chars.
|
2010-09-01 17:36:09 +02:00
|
|
|
if (value.endsWith(QLatin1Char('\'')) && type.endsWith("char")) {
|
2010-04-26 18:54:08 +02:00
|
|
|
const int blankPos = value.indexOf(QLatin1Char(' '));
|
|
|
|
|
if (blankPos != -1)
|
|
|
|
|
value.truncate(blankPos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// avoid duplicated information
|
2011-12-21 14:02:52 +01:00
|
|
|
if (value.startsWith(QLatin1Char('(')) && value.contains(QLatin1String(") 0x")))
|
|
|
|
|
value.remove(0, value.lastIndexOf(QLatin1String(") 0x")) + 2);
|
2010-04-26 18:54:08 +02:00
|
|
|
|
|
|
|
|
// doubles are sometimes displayed as "@0x6141378: 1.2".
|
|
|
|
|
// I don't want that.
|
2011-12-21 14:02:52 +01:00
|
|
|
if (/*isIntOrFloatType(type) && */ value.startsWith(QLatin1String("@0x"))
|
|
|
|
|
&& value.contains(QLatin1Char(':'))) {
|
|
|
|
|
value.remove(0, value.indexOf(QLatin1Char(':')) + 2);
|
2010-04-26 18:54:08 +02:00
|
|
|
setHasChildren(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// "numchild" is sometimes lying
|
|
|
|
|
//MODEL_DEBUG("\n\n\nPOINTER: " << type << value);
|
|
|
|
|
if (isPointerType(type))
|
2011-12-21 14:02:52 +01:00
|
|
|
setHasChildren(value != QLatin1String("0x0") && value != QLatin1String("<null>")
|
2010-04-26 18:54:08 +02:00
|
|
|
&& !isCharPointerType(type));
|
|
|
|
|
|
|
|
|
|
// pointer type information is available in the 'type'
|
|
|
|
|
// column. No need to duplicate it here.
|
2011-12-21 14:02:52 +01:00
|
|
|
if (value.startsWith(QLatin1Char('(') + QLatin1String(type) + QLatin1String(") 0x")))
|
2010-04-26 18:54:08 +02:00
|
|
|
value = value.section(QLatin1Char(' '), -1, -1);
|
|
|
|
|
|
|
|
|
|
setValueUnneeded();
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-08 15:38:50 +02:00
|
|
|
enum GuessChildrenResult { HasChildren, HasNoChildren, HasPossiblyChildren };
|
|
|
|
|
|
|
|
|
|
static GuessChildrenResult guessChildren(const QByteArray &type)
|
|
|
|
|
{
|
|
|
|
|
if (isIntOrFloatType(type))
|
|
|
|
|
return HasNoChildren;
|
|
|
|
|
if (isCharPointerType(type))
|
|
|
|
|
return HasNoChildren;
|
|
|
|
|
if (isPointerType(type))
|
|
|
|
|
return HasChildren;
|
|
|
|
|
if (type.endsWith("QString"))
|
|
|
|
|
return HasNoChildren;
|
|
|
|
|
return HasPossiblyChildren;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
void WatchData::setType(const QByteArray &str, bool guessChildrenFromType)
|
2010-04-26 18:54:08 +02:00
|
|
|
{
|
|
|
|
|
type = str.trimmed();
|
|
|
|
|
bool changed = true;
|
|
|
|
|
while (changed) {
|
2010-09-01 17:36:09 +02:00
|
|
|
if (type.endsWith("const"))
|
2010-04-26 18:54:08 +02:00
|
|
|
type.chop(5);
|
2010-09-01 17:36:09 +02:00
|
|
|
else if (type.endsWith(' '))
|
2010-04-26 18:54:08 +02:00
|
|
|
type.chop(1);
|
2010-09-01 17:36:09 +02:00
|
|
|
else if (type.startsWith("const "))
|
2010-04-26 18:54:08 +02:00
|
|
|
type = type.mid(6);
|
2010-09-01 17:36:09 +02:00
|
|
|
else if (type.startsWith("volatile "))
|
2010-04-26 18:54:08 +02:00
|
|
|
type = type.mid(9);
|
2010-09-01 17:36:09 +02:00
|
|
|
else if (type.startsWith("class "))
|
2010-04-26 18:54:08 +02:00
|
|
|
type = type.mid(6);
|
2010-09-01 17:36:09 +02:00
|
|
|
else if (type.startsWith("struct "))
|
2010-04-26 18:54:08 +02:00
|
|
|
type = type.mid(6);
|
2010-09-01 17:36:09 +02:00
|
|
|
else if (type.startsWith(' '))
|
2010-04-26 18:54:08 +02:00
|
|
|
type = type.mid(1);
|
|
|
|
|
else
|
|
|
|
|
changed = false;
|
|
|
|
|
}
|
|
|
|
|
if (guessChildrenFromType) {
|
|
|
|
|
switch (guessChildren(type)) {
|
|
|
|
|
case HasChildren:
|
|
|
|
|
setHasChildren(true);
|
|
|
|
|
break;
|
|
|
|
|
case HasNoChildren:
|
|
|
|
|
setHasChildren(false);
|
|
|
|
|
break;
|
|
|
|
|
case HasPossiblyChildren:
|
|
|
|
|
setHasChildren(true); // FIXME: bold assumption
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-22 11:19:35 +02:00
|
|
|
void WatchData::setHexAddress(const QByteArray &a)
|
|
|
|
|
{
|
|
|
|
|
bool ok;
|
2011-07-07 15:00:11 +02:00
|
|
|
const qint64 av = a.toULongLong(&ok, 0);
|
2010-09-22 11:19:35 +02:00
|
|
|
if (ok) {
|
|
|
|
|
address = av;
|
|
|
|
|
} else {
|
|
|
|
|
qWarning("WatchData::setHexAddress(): Failed to parse address value '%s' for '%s', '%s'",
|
|
|
|
|
a.constData(), iname.constData(), type.constData());
|
|
|
|
|
address = 0;
|
|
|
|
|
}
|
2010-04-26 18:54:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString WatchData::toString() const
|
|
|
|
|
{
|
|
|
|
|
const char *doubleQuoteComma = "\",";
|
|
|
|
|
QString res;
|
|
|
|
|
QTextStream str(&res);
|
|
|
|
|
str << QLatin1Char('{');
|
|
|
|
|
if (!iname.isEmpty())
|
|
|
|
|
str << "iname=\"" << iname << doubleQuoteComma;
|
2010-10-08 14:55:57 +02:00
|
|
|
str << "sortId=\"" << sortId << doubleQuoteComma;
|
2011-12-21 14:02:52 +01:00
|
|
|
if (!name.isEmpty() && name != QLatin1String(iname))
|
2010-04-26 18:54:08 +02:00
|
|
|
str << "name=\"" << name << doubleQuoteComma;
|
|
|
|
|
if (error)
|
|
|
|
|
str << "error,";
|
2010-09-22 11:19:35 +02:00
|
|
|
if (address) {
|
|
|
|
|
str.setIntegerBase(16);
|
|
|
|
|
str << "addr=\"0x" << address << doubleQuoteComma;
|
|
|
|
|
str.setIntegerBase(10);
|
|
|
|
|
}
|
2013-03-06 10:03:29 +01:00
|
|
|
if (origaddr) {
|
2011-04-06 18:39:56 +02:00
|
|
|
str.setIntegerBase(16);
|
2013-03-06 10:03:29 +01:00
|
|
|
str << "referencingaddr=\"0x" << origaddr << doubleQuoteComma;
|
2011-04-06 18:39:56 +02:00
|
|
|
str.setIntegerBase(10);
|
|
|
|
|
}
|
2010-04-26 18:54:08 +02:00
|
|
|
if (!exp.isEmpty())
|
|
|
|
|
str << "exp=\"" << exp << doubleQuoteComma;
|
|
|
|
|
|
|
|
|
|
if (!variable.isEmpty())
|
|
|
|
|
str << "variable=\"" << variable << doubleQuoteComma;
|
|
|
|
|
|
|
|
|
|
if (isValueNeeded())
|
|
|
|
|
str << "value=<needed>,";
|
2015-03-19 10:04:58 +01:00
|
|
|
if (!value.isEmpty())
|
2010-04-26 18:54:08 +02:00
|
|
|
str << "value=\"" << value << doubleQuoteComma;
|
|
|
|
|
|
2014-05-16 00:18:17 +02:00
|
|
|
if (elided)
|
|
|
|
|
str << "valueelided=\"" << elided << doubleQuoteComma;
|
|
|
|
|
|
2010-04-26 18:54:08 +02:00
|
|
|
if (!editvalue.isEmpty())
|
|
|
|
|
str << "editvalue=\"<...>\",";
|
|
|
|
|
// str << "editvalue=\"" << editvalue << doubleQuoteComma;
|
|
|
|
|
|
2010-09-22 11:19:35 +02:00
|
|
|
if (!dumperFlags.isEmpty())
|
|
|
|
|
str << "dumperFlags=\"" << dumperFlags << doubleQuoteComma;
|
|
|
|
|
|
2015-03-19 10:04:58 +01:00
|
|
|
str << "type=\"" << type << doubleQuoteComma;
|
2010-04-26 18:54:08 +02:00
|
|
|
|
2015-03-19 10:04:58 +01:00
|
|
|
str << "hasChildren=\"" << (hasChildren ? "true" : "false") << doubleQuoteComma;
|
2010-04-26 18:54:08 +02:00
|
|
|
|
|
|
|
|
if (isChildrenNeeded())
|
|
|
|
|
str << "children=<needed>,";
|
|
|
|
|
str.flush();
|
|
|
|
|
if (res.endsWith(QLatin1Char(',')))
|
|
|
|
|
res.truncate(res.size() - 1);
|
|
|
|
|
return res + QLatin1Char('}');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Format a tooltip fow with aligned colon.
|
|
|
|
|
static void formatToolTipRow(QTextStream &str,
|
|
|
|
|
const QString &category, const QString &value)
|
|
|
|
|
{
|
2014-08-28 17:33:47 +02:00
|
|
|
QString val = value.toHtmlEscaped();
|
2012-09-04 12:49:24 +02:00
|
|
|
val.replace(QLatin1Char('\n'), QLatin1String("<br>"));
|
2010-04-26 18:54:08 +02:00
|
|
|
str << "<tr><td>" << category << "</td><td> : </td><td>"
|
2012-09-04 12:49:24 +02:00
|
|
|
<< val << "</td></tr>";
|
2010-04-26 18:54:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString WatchData::toToolTip() const
|
|
|
|
|
{
|
|
|
|
|
QString res;
|
|
|
|
|
QTextStream str(&res);
|
|
|
|
|
str << "<html><body><table>";
|
2010-10-15 13:00:14 +02:00
|
|
|
formatToolTipRow(str, tr("Name"), name);
|
2011-12-21 14:02:52 +01:00
|
|
|
formatToolTipRow(str, tr("Expression"), QLatin1String(exp));
|
|
|
|
|
formatToolTipRow(str, tr("Internal Type"), QLatin1String(type));
|
2013-07-05 11:10:15 +02:00
|
|
|
if (!displayedType.isEmpty())
|
|
|
|
|
formatToolTipRow(str, tr("Displayed Type"), displayedType);
|
2014-05-16 00:18:17 +02:00
|
|
|
QString val = value;
|
2013-07-05 15:51:28 +02:00
|
|
|
// Automatically display hex value for unsigned integers.
|
|
|
|
|
if (!val.isEmpty() && val.at(0).isDigit() && isIntType(type)) {
|
|
|
|
|
bool ok;
|
|
|
|
|
const quint64 intValue = val.toULongLong(&ok);
|
|
|
|
|
if (ok && intValue)
|
|
|
|
|
val += QLatin1String(" (hex) ") + QString::number(intValue, 16);
|
|
|
|
|
}
|
2012-09-04 12:49:24 +02:00
|
|
|
if (val.size() > 1000) {
|
2010-04-26 18:54:08 +02:00
|
|
|
val.truncate(1000);
|
2013-10-17 14:52:10 +02:00
|
|
|
val += QLatin1Char(' ');
|
|
|
|
|
val += tr("... <cut off>");
|
2010-04-26 18:54:08 +02:00
|
|
|
}
|
2010-10-15 13:00:14 +02:00
|
|
|
formatToolTipRow(str, tr("Value"), val);
|
2013-03-06 10:03:29 +01:00
|
|
|
if (address)
|
|
|
|
|
formatToolTipRow(str, tr("Object Address"), formatToolTipAddress(address));
|
|
|
|
|
if (origaddr)
|
|
|
|
|
formatToolTipRow(str, tr("Pointer Address"), formatToolTipAddress(origaddr));
|
2011-04-04 16:15:03 +02:00
|
|
|
if (size)
|
2012-07-27 19:21:03 +02:00
|
|
|
formatToolTipRow(str, tr("Static Object Size"), tr("%n bytes", 0, size));
|
2011-12-21 14:02:52 +01:00
|
|
|
formatToolTipRow(str, tr("Internal ID"), QLatin1String(iname));
|
2010-04-26 18:54:08 +02:00
|
|
|
str << "</table></body></html>";
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString WatchData::msgNotInScope()
|
|
|
|
|
{
|
2011-05-11 15:24:50 +02:00
|
|
|
//: Value of variable in Debugger Locals display for variables out
|
|
|
|
|
//: of scope (stopped above initialization).
|
|
|
|
|
static const QString rc =
|
|
|
|
|
QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
|
2010-04-26 18:54:08 +02:00
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString &WatchData::shadowedNameFormat()
|
|
|
|
|
{
|
2010-09-10 10:51:43 +02:00
|
|
|
//: Display of variables shadowed by variables of the same name
|
|
|
|
|
//: in nested scopes: Variable %1 is the variable name, %2 is a
|
|
|
|
|
//: simple count.
|
2011-05-11 15:24:50 +02:00
|
|
|
static const QString format =
|
|
|
|
|
QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>");
|
2010-04-26 18:54:08 +02:00
|
|
|
return format;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString WatchData::shadowedName(const QString &name, int seen)
|
|
|
|
|
{
|
|
|
|
|
if (seen <= 0)
|
|
|
|
|
return name;
|
2011-04-12 11:57:57 +02:00
|
|
|
return shadowedNameFormat().arg(name).arg(seen);
|
2010-04-26 18:54:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-09-22 11:19:35 +02:00
|
|
|
QByteArray WatchData::hexAddress() const
|
|
|
|
|
{
|
2011-05-11 15:24:50 +02:00
|
|
|
if (address)
|
|
|
|
|
return QByteArray("0x") + QByteArray::number(address, 16);
|
|
|
|
|
return QByteArray();
|
2010-06-16 11:08:54 +02:00
|
|
|
}
|
|
|
|
|
|
2013-02-01 13:05:28 +01:00
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Protocol convienience
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void WatchData::updateValue(const GdbMi &item)
|
|
|
|
|
{
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi value = item["value"];
|
2013-02-01 13:05:28 +01:00
|
|
|
if (value.isValid()) {
|
2013-05-07 12:09:54 +02:00
|
|
|
int encoding = item["valueencoded"].toInt();
|
2013-02-01 13:05:28 +01:00
|
|
|
setValue(decodeData(value.data(), encoding));
|
|
|
|
|
} else {
|
|
|
|
|
setValueNeeded();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchData::updateChildCount(const GdbMi &mi)
|
|
|
|
|
{
|
|
|
|
|
if (mi.isValid())
|
2013-05-07 12:09:54 +02:00
|
|
|
setHasChildren(mi.toInt() > 0);
|
2013-02-01 13:05:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void setWatchDataValueEnabled(WatchData &data, const GdbMi &mi)
|
|
|
|
|
{
|
|
|
|
|
if (mi.data() == "true")
|
|
|
|
|
data.valueEnabled = true;
|
|
|
|
|
else if (mi.data() == "false")
|
|
|
|
|
data.valueEnabled = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void setWatchDataValueEditable(WatchData &data, const GdbMi &mi)
|
|
|
|
|
{
|
|
|
|
|
if (mi.data() == "true")
|
|
|
|
|
data.valueEditable = true;
|
|
|
|
|
else if (mi.data() == "false")
|
|
|
|
|
data.valueEditable = false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-06 10:03:29 +01:00
|
|
|
static void setWatchDataAddress(WatchData &data, quint64 address)
|
2013-02-01 13:05:28 +01:00
|
|
|
{
|
2013-03-06 10:03:29 +01:00
|
|
|
data.address = address;
|
2013-02-01 13:05:28 +01:00
|
|
|
|
|
|
|
|
if (data.exp.isEmpty() && !data.dumperFlags.startsWith('$')) {
|
|
|
|
|
if (data.iname.startsWith("local.") && data.iname.count('.') == 1)
|
|
|
|
|
// Solve one common case of adding 'class' in
|
|
|
|
|
// *(class X*)0xdeadbeef for gdb.
|
|
|
|
|
data.exp = data.name.toLatin1();
|
|
|
|
|
else
|
|
|
|
|
data.exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.hexAddress();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-06 10:03:29 +01:00
|
|
|
void WatchData::updateAddress(const GdbMi &mi)
|
2013-02-01 13:05:28 +01:00
|
|
|
{
|
2013-03-06 10:03:29 +01:00
|
|
|
if (!mi.isValid())
|
2013-02-01 13:05:28 +01:00
|
|
|
return;
|
2013-03-06 10:03:29 +01:00
|
|
|
const QByteArray addressBA = mi.data();
|
2013-02-01 13:05:28 +01:00
|
|
|
if (!addressBA.startsWith("0x")) { // Item model dumpers pull tricks.
|
|
|
|
|
dumperFlags = addressBA;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-03-06 10:03:29 +01:00
|
|
|
const quint64 address = mi.toAddress();
|
|
|
|
|
setWatchDataAddress(*this, address);
|
2013-02-01 13:05:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void setWatchDataSize(WatchData &data, const GdbMi &mi)
|
|
|
|
|
{
|
|
|
|
|
if (mi.isValid()) {
|
|
|
|
|
bool ok = false;
|
|
|
|
|
const unsigned size = mi.data().toUInt(&ok);
|
|
|
|
|
if (ok)
|
|
|
|
|
data.size = size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the "type" and "displayedtype" children of root and set up type.
|
|
|
|
|
void WatchData::updateType(const GdbMi &item)
|
|
|
|
|
{
|
|
|
|
|
if (item.isValid())
|
|
|
|
|
setType(item.data());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchData::updateDisplayedType(const GdbMi &item)
|
|
|
|
|
{
|
|
|
|
|
if (item.isValid())
|
|
|
|
|
displayedType = QString::fromLatin1(item.data());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Utilities to decode string data returned by the dumper helpers.
|
|
|
|
|
|
2014-09-19 22:52:08 +02:00
|
|
|
template <class T>
|
|
|
|
|
QString decodeItemHelper(const T &t)
|
|
|
|
|
{
|
|
|
|
|
return QString::number(t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString decodeItemHelper(const double &t)
|
|
|
|
|
{
|
|
|
|
|
return QString::number(t, 'g', 16);
|
|
|
|
|
}
|
2013-02-01 13:05:28 +01:00
|
|
|
|
|
|
|
|
template <class T>
|
2015-01-28 15:14:15 +01:00
|
|
|
void decodeArrayHelper(std::function<void(const WatchData &)> itemHandler, const WatchData &tmplate,
|
2013-02-01 13:05:28 +01:00
|
|
|
const QByteArray &rawData)
|
|
|
|
|
{
|
|
|
|
|
const QByteArray ba = QByteArray::fromHex(rawData);
|
|
|
|
|
const T *p = (const T *) ba.data();
|
|
|
|
|
WatchData data;
|
|
|
|
|
const QByteArray exp = "*(" + gdbQuoteTypes(tmplate.type) + "*)0x";
|
|
|
|
|
for (int i = 0, n = ba.size() / sizeof(T); i < n; ++i) {
|
|
|
|
|
data = tmplate;
|
|
|
|
|
data.sortId = i;
|
|
|
|
|
data.iname += QByteArray::number(i);
|
|
|
|
|
data.name = QString::fromLatin1("[%1]").arg(i);
|
2014-09-19 22:52:08 +02:00
|
|
|
data.value = decodeItemHelper(p[i]);
|
2013-02-01 13:05:28 +01:00
|
|
|
data.address += i * sizeof(T);
|
|
|
|
|
data.exp = exp + QByteArray::number(data.address, 16);
|
|
|
|
|
data.setAllUnneeded();
|
2015-01-28 15:14:15 +01:00
|
|
|
itemHandler(data);
|
2013-02-01 13:05:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-28 15:14:15 +01:00
|
|
|
void decodeArrayData(std::function<void(const WatchData &)> itemHandler, const WatchData &tmplate,
|
2013-02-01 13:05:28 +01:00
|
|
|
const QByteArray &rawData, int encoding)
|
|
|
|
|
{
|
|
|
|
|
switch (encoding) {
|
|
|
|
|
case Hex2EncodedInt1:
|
2015-01-28 15:14:15 +01:00
|
|
|
decodeArrayHelper<signed char>(itemHandler, tmplate, rawData);
|
2013-02-01 13:05:28 +01:00
|
|
|
break;
|
|
|
|
|
case Hex2EncodedInt2:
|
2015-01-28 15:14:15 +01:00
|
|
|
decodeArrayHelper<short>(itemHandler, tmplate, rawData);
|
2013-02-01 13:05:28 +01:00
|
|
|
break;
|
|
|
|
|
case Hex2EncodedInt4:
|
2015-01-28 15:14:15 +01:00
|
|
|
decodeArrayHelper<int>(itemHandler, tmplate, rawData);
|
2013-02-01 13:05:28 +01:00
|
|
|
break;
|
|
|
|
|
case Hex2EncodedInt8:
|
2015-01-28 15:14:15 +01:00
|
|
|
decodeArrayHelper<qint64>(itemHandler, tmplate, rawData);
|
2013-02-01 13:05:28 +01:00
|
|
|
break;
|
|
|
|
|
case Hex2EncodedUInt1:
|
2015-01-28 15:14:15 +01:00
|
|
|
decodeArrayHelper<uchar>(itemHandler, tmplate, rawData);
|
2013-02-01 13:05:28 +01:00
|
|
|
break;
|
|
|
|
|
case Hex2EncodedUInt2:
|
2015-01-28 15:14:15 +01:00
|
|
|
decodeArrayHelper<ushort>(itemHandler, tmplate, rawData);
|
2013-02-01 13:05:28 +01:00
|
|
|
break;
|
|
|
|
|
case Hex2EncodedUInt4:
|
2015-01-28 15:14:15 +01:00
|
|
|
decodeArrayHelper<uint>(itemHandler, tmplate, rawData);
|
2013-02-01 13:05:28 +01:00
|
|
|
break;
|
|
|
|
|
case Hex2EncodedUInt8:
|
2015-01-28 15:14:15 +01:00
|
|
|
decodeArrayHelper<quint64>(itemHandler, tmplate, rawData);
|
2013-02-01 13:05:28 +01:00
|
|
|
break;
|
|
|
|
|
case Hex2EncodedFloat4:
|
2015-01-28 15:14:15 +01:00
|
|
|
decodeArrayHelper<float>(itemHandler, tmplate, rawData);
|
2013-02-01 13:05:28 +01:00
|
|
|
break;
|
|
|
|
|
case Hex2EncodedFloat8:
|
2015-01-28 15:14:15 +01:00
|
|
|
decodeArrayHelper<double>(itemHandler, tmplate, rawData);
|
2013-02-01 13:05:28 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
qDebug() << "ENCODING ERROR: " << encoding;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 13:25:35 +01:00
|
|
|
void parseChildrenData(const WatchData &data0, const GdbMi &item,
|
2015-01-28 10:20:01 +01:00
|
|
|
std::function<void(const WatchData &)> itemHandler,
|
2015-01-29 13:25:35 +01:00
|
|
|
std::function<void(const WatchData &, const GdbMi &)> childHandler,
|
2015-01-28 10:20:01 +01:00
|
|
|
std::function<void(const WatchData &childTemplate, const QByteArray &encodedData, int encoding)> arrayDecoder)
|
2013-02-01 13:05:28 +01:00
|
|
|
{
|
|
|
|
|
//qDebug() << "HANDLE CHILDREN: " << data0.toString() << item.toString();
|
|
|
|
|
WatchData data = data0;
|
2015-01-29 13:25:35 +01:00
|
|
|
data.setChildrenUnneeded();
|
2013-02-01 13:05:28 +01:00
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi children = item["children"];
|
2013-02-01 13:05:28 +01:00
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
data.updateType(item["type"]);
|
|
|
|
|
GdbMi mi = item["editvalue"];
|
2013-02-01 13:05:28 +01:00
|
|
|
if (mi.isValid())
|
|
|
|
|
data.editvalue = mi.data();
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
mi = item["editformat"];
|
2014-05-27 22:35:54 +02:00
|
|
|
data.editformat = mi.toInt();
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
mi = item["typeformats"];
|
2013-02-01 13:05:28 +01:00
|
|
|
if (mi.isValid())
|
|
|
|
|
data.typeFormats = QString::fromUtf8(mi.data());
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2014-05-16 00:18:17 +02:00
|
|
|
mi = item["valueelided"];
|
|
|
|
|
if (mi.isValid())
|
|
|
|
|
data.elided = mi.toInt();
|
|
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
mi = item["bitpos"];
|
2013-02-01 13:05:28 +01:00
|
|
|
if (mi.isValid())
|
2013-05-07 12:09:54 +02:00
|
|
|
data.bitpos = mi.toInt();
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
mi = item["bitsize"];
|
2013-02-01 13:05:28 +01:00
|
|
|
if (mi.isValid())
|
2013-05-07 12:09:54 +02:00
|
|
|
data.bitsize = mi.toInt();
|
2013-02-01 13:05:28 +01:00
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
mi = item["origaddr"];
|
2013-03-06 10:03:29 +01:00
|
|
|
if (mi.isValid())
|
|
|
|
|
data.origaddr = mi.toAddress();
|
|
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
data.updateAddress(item["addr"]);
|
2013-02-01 13:05:28 +01:00
|
|
|
data.updateValue(item);
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
setWatchDataSize(data, item["size"]);
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
mi = item["exp"];
|
2013-03-06 10:03:29 +01:00
|
|
|
if (mi.isValid())
|
|
|
|
|
data.exp = mi.data();
|
|
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
setWatchDataValueEnabled(data, item["valueenabled"]);
|
|
|
|
|
setWatchDataValueEditable(data, item["valueeditable"]);
|
|
|
|
|
data.updateChildCount(item["numchild"]);
|
2013-02-01 13:05:28 +01:00
|
|
|
//qDebug() << "\nAPPEND TO LIST: " << data.toString() << "\n";
|
2015-01-28 10:20:01 +01:00
|
|
|
itemHandler(data);
|
2013-02-01 13:05:28 +01:00
|
|
|
|
|
|
|
|
bool ok = false;
|
2013-05-03 18:26:10 +02:00
|
|
|
qulonglong addressBase = item["addrbase"].data().toULongLong(&ok, 0);
|
|
|
|
|
qulonglong addressStep = item["addrstep"].data().toULongLong(&ok, 0);
|
2013-02-01 13:05:28 +01:00
|
|
|
|
|
|
|
|
// Try not to repeat data too often.
|
|
|
|
|
WatchData childtemplate;
|
2013-05-03 18:26:10 +02:00
|
|
|
childtemplate.updateType(item["childtype"]);
|
|
|
|
|
childtemplate.updateChildCount(item["childnumchild"]);
|
2013-02-01 13:05:28 +01:00
|
|
|
//qDebug() << "CHILD TEMPLATE:" << childtemplate.toString();
|
|
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
mi = item["arraydata"];
|
2013-02-01 13:05:28 +01:00
|
|
|
if (mi.isValid()) {
|
2013-05-07 12:09:54 +02:00
|
|
|
int encoding = item["arrayencoding"].toInt();
|
2013-02-01 13:05:28 +01:00
|
|
|
childtemplate.iname = data.iname + '.';
|
|
|
|
|
childtemplate.address = addressBase;
|
2015-01-28 10:20:01 +01:00
|
|
|
arrayDecoder(childtemplate, mi.data(), encoding);
|
2013-02-01 13:05:28 +01:00
|
|
|
} else {
|
2015-02-19 16:44:58 +01:00
|
|
|
for (int i = 0, n = int(children.children().size()); i != n; ++i) {
|
2013-02-01 13:05:28 +01:00
|
|
|
const GdbMi &child = children.children().at(i);
|
|
|
|
|
WatchData data1 = childtemplate;
|
|
|
|
|
data1.sortId = i;
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi name = child["name"];
|
2013-02-01 13:05:28 +01:00
|
|
|
if (name.isValid())
|
|
|
|
|
data1.name = QString::fromLatin1(name.data());
|
|
|
|
|
else
|
|
|
|
|
data1.name = QString::number(i);
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi iname = child["iname"];
|
2013-02-01 13:05:28 +01:00
|
|
|
if (iname.isValid()) {
|
|
|
|
|
data1.iname = iname.data();
|
|
|
|
|
} else {
|
|
|
|
|
data1.iname = data.iname;
|
|
|
|
|
data1.iname += '.';
|
|
|
|
|
data1.iname += data1.name.toLatin1();
|
|
|
|
|
}
|
|
|
|
|
if (!data1.name.isEmpty() && data1.name.at(0).isDigit())
|
|
|
|
|
data1.name = QLatin1Char('[') + data1.name + QLatin1Char(']');
|
|
|
|
|
if (addressStep) {
|
|
|
|
|
setWatchDataAddress(data1, addressBase);
|
|
|
|
|
addressBase += addressStep;
|
|
|
|
|
}
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray key = child["key"].data();
|
2013-02-01 13:05:28 +01:00
|
|
|
if (!key.isEmpty()) {
|
2013-05-07 12:09:54 +02:00
|
|
|
int encoding = child["keyencoded"].toInt();
|
2013-06-13 17:49:41 +02:00
|
|
|
data1.name = decodeData(key, encoding);
|
2013-02-01 13:05:28 +01:00
|
|
|
}
|
2015-01-29 13:25:35 +01:00
|
|
|
childHandler(data1, child);
|
2013-02-01 13:05:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 13:25:35 +01:00
|
|
|
void parseWatchData(const WatchData &data0, const GdbMi &input,
|
2015-01-28 10:20:01 +01:00
|
|
|
QList<WatchData> *list)
|
|
|
|
|
{
|
|
|
|
|
auto itemHandler = [list](const WatchData &data) {
|
|
|
|
|
list->append(data);
|
|
|
|
|
};
|
2015-01-29 13:25:35 +01:00
|
|
|
auto childHandler = [list](const WatchData &innerData, const GdbMi &innerInput) {
|
|
|
|
|
parseWatchData(innerData, innerInput, list);
|
2015-01-28 10:20:01 +01:00
|
|
|
};
|
2015-01-28 15:14:15 +01:00
|
|
|
auto arrayDecoder = [itemHandler](const WatchData &childTemplate,
|
2015-01-28 10:20:01 +01:00
|
|
|
const QByteArray &encodedData, int encoding) {
|
2015-01-28 15:14:15 +01:00
|
|
|
decodeArrayData(itemHandler, childTemplate, encodedData, encoding);
|
2015-01-28 10:20:01 +01:00
|
|
|
};
|
|
|
|
|
|
2015-01-29 13:25:35 +01:00
|
|
|
parseChildrenData(data0, input, itemHandler, childHandler, arrayDecoder);
|
2015-01-28 10:20:01 +01:00
|
|
|
}
|
|
|
|
|
|
2010-04-26 18:54:08 +02:00
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Debugger
|
|
|
|
|
|