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
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
WatchItem::WatchItem() :
|
|
|
|
|
id(WatchItem::InvalidId),
|
2010-10-15 13:00:14 +02:00
|
|
|
state(InitialState),
|
2015-03-25 11:51:14 +01:00
|
|
|
editformat(StopDisplay),
|
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),
|
2015-12-16 17:17:38 +01:00
|
|
|
arrayIndex(-1),
|
2015-03-19 12:42:53 +01:00
|
|
|
wantsChildren(false),
|
2010-04-26 18:54:08 +02:00
|
|
|
valueEnabled(true),
|
2015-07-06 09:46:08 +02:00
|
|
|
valueEditable(true),
|
|
|
|
|
outdated(false)
|
2010-04-26 18:54:08 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
bool WatchItem::isVTablePointer() const
|
2011-11-11 17:51:29 +01:00
|
|
|
{
|
|
|
|
|
// 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]"));
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
void WatchItem::setError(const QString &msg)
|
2010-04-26 18:54:08 +02:00
|
|
|
{
|
|
|
|
|
setAllUnneeded();
|
|
|
|
|
value = msg;
|
2015-03-19 12:42:53 +01:00
|
|
|
wantsChildren = false;
|
2010-04-26 18:54:08 +02:00
|
|
|
valueEnabled = false;
|
|
|
|
|
valueEditable = false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
void WatchItem::setValue(const QString &value0)
|
2010-04-26 18:54:08 +02:00
|
|
|
{
|
|
|
|
|
value = value0;
|
2011-12-21 14:02:52 +01:00
|
|
|
if (value == QLatin1String("{...}")) {
|
2010-04-26 18:54:08 +02:00
|
|
|
value.clear();
|
2015-03-19 12:42:53 +01:00
|
|
|
wantsChildren = true; // at least one...
|
2010-04-26 18:54:08 +02:00
|
|
|
}
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
void WatchItem::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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
QString WatchItem::toString() const
|
2010-04-26 18:54:08 +02:00
|
|
|
{
|
|
|
|
|
const char *doubleQuoteComma = "\",";
|
|
|
|
|
QString res;
|
|
|
|
|
QTextStream str(&res);
|
|
|
|
|
str << QLatin1Char('{');
|
|
|
|
|
if (!iname.isEmpty())
|
|
|
|
|
str << "iname=\"" << iname << 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;
|
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 (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=\"<...>\",";
|
|
|
|
|
|
2015-03-19 10:04:58 +01:00
|
|
|
str << "type=\"" << type << doubleQuoteComma;
|
2010-04-26 18:54:08 +02:00
|
|
|
|
2015-03-19 12:42:53 +01:00
|
|
|
str << "wantsChildren=\"" << (wantsChildren ? "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('}');
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
QString WatchItem::msgNotInScope()
|
2010-04-26 18:54:08 +02:00
|
|
|
{
|
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 =
|
2015-12-16 17:17:38 +01:00
|
|
|
QCoreApplication::translate("Debugger::Internal::WatchItem", "<not in scope>");
|
2010-04-26 18:54:08 +02:00
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
const QString &WatchItem::shadowedNameFormat()
|
2010-04-26 18:54:08 +02:00
|
|
|
{
|
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 =
|
2015-12-16 17:17:38 +01:00
|
|
|
QCoreApplication::translate("Debugger::Internal::WatchItem", "%1 <shadowed %2>");
|
2010-04-26 18:54:08 +02:00
|
|
|
return format;
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
QString WatchItem::shadowedName(const QString &name, int seen)
|
2010-04-26 18:54:08 +02:00
|
|
|
{
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
QByteArray WatchItem::hexAddress() const
|
2010-09-22 11:19:35 +02:00
|
|
|
{
|
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
|
|
|
}
|
|
|
|
|
|
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-12-16 17:17:38 +01:00
|
|
|
void decodeArrayHelper(WatchItem *item, const QByteArray &rawData, int size, const QByteArray &childType)
|
2013-02-01 13:05:28 +01:00
|
|
|
{
|
|
|
|
|
const QByteArray ba = QByteArray::fromHex(rawData);
|
|
|
|
|
const T *p = (const T *) ba.data();
|
|
|
|
|
for (int i = 0, n = ba.size() / sizeof(T); i < n; ++i) {
|
2015-12-16 17:17:38 +01:00
|
|
|
WatchItem *child = new WatchItem;
|
|
|
|
|
child->arrayIndex = i;
|
|
|
|
|
child->value = decodeItemHelper(p[i]);
|
|
|
|
|
child->size = size;
|
|
|
|
|
child->type = childType;
|
|
|
|
|
child->setAllUnneeded();
|
|
|
|
|
item->appendChild(child);
|
2013-02-01 13:05:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
static void decodeArrayData(WatchItem *item, const QByteArray &rawData,
|
|
|
|
|
const DebuggerEncoding &encoding, const QByteArray &childType)
|
2015-12-11 13:28:21 +01:00
|
|
|
{
|
|
|
|
|
switch (encoding.type) {
|
|
|
|
|
case DebuggerEncoding::HexEncodedSignedInteger:
|
|
|
|
|
switch (encoding.size) {
|
|
|
|
|
case 1:
|
2015-12-16 17:17:38 +01:00
|
|
|
return decodeArrayHelper<signed char>(item, rawData, encoding.size, childType);
|
2015-12-11 13:28:21 +01:00
|
|
|
case 2:
|
2015-12-16 17:17:38 +01:00
|
|
|
return decodeArrayHelper<short>(item, rawData, encoding.size, childType);
|
2015-12-11 13:28:21 +01:00
|
|
|
case 4:
|
2015-12-16 17:17:38 +01:00
|
|
|
return decodeArrayHelper<int>(item, rawData, encoding.size, childType);
|
2015-12-11 13:28:21 +01:00
|
|
|
case 8:
|
2015-12-16 17:17:38 +01:00
|
|
|
return decodeArrayHelper<qint64>(item, rawData, encoding.size, childType);
|
2015-12-11 13:28:21 +01:00
|
|
|
}
|
|
|
|
|
case DebuggerEncoding::HexEncodedUnsignedInteger:
|
|
|
|
|
switch (encoding.size) {
|
|
|
|
|
case 1:
|
2015-12-16 17:17:38 +01:00
|
|
|
return decodeArrayHelper<uchar>(item, rawData, encoding.size, childType);
|
2015-12-11 13:28:21 +01:00
|
|
|
case 2:
|
2015-12-16 17:17:38 +01:00
|
|
|
return decodeArrayHelper<ushort>(item, rawData, encoding.size, childType);
|
2015-12-11 13:28:21 +01:00
|
|
|
case 4:
|
2015-12-16 17:17:38 +01:00
|
|
|
return decodeArrayHelper<uint>(item, rawData, encoding.size, childType);
|
2015-12-11 13:28:21 +01:00
|
|
|
case 8:
|
2015-12-16 17:17:38 +01:00
|
|
|
return decodeArrayHelper<quint64>(item, rawData, encoding.size, childType);
|
2015-12-11 13:28:21 +01:00
|
|
|
}
|
2013-02-01 13:05:28 +01:00
|
|
|
break;
|
2015-12-11 13:28:21 +01:00
|
|
|
case DebuggerEncoding::HexEncodedFloat:
|
|
|
|
|
switch (encoding.size) {
|
|
|
|
|
case 4:
|
2015-12-16 17:17:38 +01:00
|
|
|
return decodeArrayHelper<float>(item, rawData, encoding.size, childType);
|
2015-12-11 13:28:21 +01:00
|
|
|
case 8:
|
2015-12-16 17:17:38 +01:00
|
|
|
return decodeArrayHelper<double>(item, rawData, encoding.size, childType);
|
2015-12-11 13:28:21 +01:00
|
|
|
}
|
2013-02-01 13:05:28 +01:00
|
|
|
default:
|
2015-12-11 13:28:21 +01:00
|
|
|
break;
|
2013-02-01 13:05:28 +01:00
|
|
|
}
|
2015-12-11 13:28:21 +01:00
|
|
|
qDebug() << "ENCODING ERROR: " << encoding.toString();
|
2013-02-01 13:05:28 +01:00
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
void WatchItem::parseHelper(const GdbMi &input)
|
2013-02-01 13:05:28 +01:00
|
|
|
{
|
2015-12-16 17:17:38 +01:00
|
|
|
setChildrenUnneeded();
|
2013-02-01 13:05:28 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
GdbMi mi = input["type"];
|
|
|
|
|
if (mi.isValid())
|
|
|
|
|
setType(mi.data());
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
editvalue = input["editvalue"].data();
|
|
|
|
|
editformat = DebuggerDisplay(input["editformat"].toInt());
|
|
|
|
|
editencoding = DebuggerEncoding(input["editencoding"].data());
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
mi = input["valueelided"];
|
2014-05-16 00:18:17 +02:00
|
|
|
if (mi.isValid())
|
2015-12-16 17:17:38 +01:00
|
|
|
elided = mi.toInt();
|
2014-05-16 00:18:17 +02:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
mi = input["bitpos"];
|
2013-02-01 13:05:28 +01:00
|
|
|
if (mi.isValid())
|
2015-12-16 17:17:38 +01:00
|
|
|
bitpos = mi.toInt();
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
mi = input["bitsize"];
|
2013-02-01 13:05:28 +01:00
|
|
|
if (mi.isValid())
|
2015-12-16 17:17:38 +01:00
|
|
|
bitsize = mi.toInt();
|
2013-02-01 13:05:28 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
mi = input["origaddr"];
|
2013-03-06 10:03:29 +01:00
|
|
|
if (mi.isValid())
|
2015-12-16 17:17:38 +01:00
|
|
|
origaddr = mi.toAddress();
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
mi = input["address"];
|
|
|
|
|
if (mi.isValid()) {
|
|
|
|
|
address = mi.toAddress();
|
|
|
|
|
if (exp.isEmpty()) {
|
|
|
|
|
if (iname.startsWith("local.") && iname.count('.') == 1)
|
|
|
|
|
// Solve one common case of adding 'class' in
|
|
|
|
|
// *(class X*)0xdeadbeef for gdb.
|
|
|
|
|
exp = name.toLatin1();
|
|
|
|
|
else
|
|
|
|
|
exp = "*(" + gdbQuoteTypes(type) + "*)" + hexAddress();
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-03-25 13:16:41 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
mi = input["value"];
|
|
|
|
|
QByteArray enc = input["valueencoded"].data();
|
|
|
|
|
if (mi.isValid() || !enc.isEmpty()) {
|
|
|
|
|
setValue(decodeData(mi.data(), enc));
|
|
|
|
|
} else {
|
|
|
|
|
setValueNeeded();
|
|
|
|
|
}
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
mi = input["size"];
|
|
|
|
|
if (mi.isValid())
|
|
|
|
|
size = mi.toInt();
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
mi = input["exp"];
|
2013-03-06 10:03:29 +01:00
|
|
|
if (mi.isValid())
|
2015-12-16 17:17:38 +01:00
|
|
|
exp = mi.data();
|
2013-03-06 10:03:29 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
mi = input["valueenabled"];
|
|
|
|
|
if (mi.data() == "true")
|
|
|
|
|
valueEnabled = true;
|
|
|
|
|
else if (mi.data() == "false")
|
|
|
|
|
valueEnabled = false;
|
2013-02-01 13:05:28 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
mi = input["valueeditable"];
|
|
|
|
|
if (mi.data() == "true")
|
|
|
|
|
valueEditable = true;
|
|
|
|
|
else if (mi.data() == "false")
|
|
|
|
|
valueEditable = false;
|
2013-02-01 13:05:28 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
mi = input["numchild"]; // GDB/MI
|
|
|
|
|
if (mi.isValid())
|
|
|
|
|
setHasChildren(mi.toInt() > 0);
|
|
|
|
|
mi = input["haschild"]; // native-mixed
|
|
|
|
|
if (mi.isValid())
|
|
|
|
|
setHasChildren(mi.toInt() > 0);
|
2013-02-01 13:05:28 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
mi = input["arraydata"];
|
2013-02-01 13:05:28 +01:00
|
|
|
if (mi.isValid()) {
|
2015-12-16 17:17:38 +01:00
|
|
|
DebuggerEncoding encoding(input["arrayencoding"].data());
|
|
|
|
|
QByteArray childType = input["childtype"].data();
|
|
|
|
|
decodeArrayData(this, mi.data(), encoding, childType);
|
2013-02-01 13:05:28 +01:00
|
|
|
} else {
|
2015-12-16 17:17:38 +01:00
|
|
|
const GdbMi children = input["children"];
|
|
|
|
|
if (children.isValid()) {
|
|
|
|
|
bool ok = false;
|
|
|
|
|
// Try not to repeat data too often.
|
|
|
|
|
const GdbMi childType = input["childtype"];
|
|
|
|
|
const GdbMi childNumChild = input["childnumchild"];
|
|
|
|
|
|
|
|
|
|
qulonglong addressBase = input["addrbase"].data().toULongLong(&ok, 0);
|
|
|
|
|
qulonglong addressStep = input["addrstep"].data().toULongLong(&ok, 0);
|
|
|
|
|
|
|
|
|
|
for (int i = 0, n = int(children.children().size()); i != n; ++i) {
|
|
|
|
|
const GdbMi &subinput = children.children().at(i);
|
|
|
|
|
WatchItem *child = new WatchItem;
|
|
|
|
|
if (childType.isValid())
|
|
|
|
|
child->setType(childType.data());
|
|
|
|
|
if (childNumChild.isValid())
|
|
|
|
|
child->setHasChildren(childNumChild.toInt() > 0);
|
|
|
|
|
GdbMi name = subinput["name"];
|
|
|
|
|
QByteArray nn;
|
|
|
|
|
if (name.isValid()) {
|
|
|
|
|
nn = name.data();
|
|
|
|
|
child->name = QString::fromLatin1(nn);
|
|
|
|
|
} else {
|
|
|
|
|
nn.setNum(i);
|
|
|
|
|
child->name = QString::fromLatin1("[%1]").arg(i);
|
|
|
|
|
}
|
|
|
|
|
GdbMi iname = subinput["iname"];
|
|
|
|
|
if (iname.isValid())
|
|
|
|
|
child->iname = iname.data();
|
|
|
|
|
else
|
|
|
|
|
child->iname = this->iname + '.' + nn;
|
|
|
|
|
if (addressStep) {
|
|
|
|
|
child->address = addressBase + i * addressStep;
|
|
|
|
|
child->exp = "*(" + gdbQuoteTypes(child->type) + "*)" + child->hexAddress();
|
|
|
|
|
}
|
|
|
|
|
QByteArray key = subinput["key"].data();
|
|
|
|
|
if (!key.isEmpty())
|
|
|
|
|
child->name = decodeData(key, subinput["keyencoded"].data());
|
|
|
|
|
child->parseHelper(subinput);
|
|
|
|
|
appendChild(child);
|
2013-02-01 13:05:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
void WatchItem::parse(const GdbMi &data)
|
2015-01-28 10:20:01 +01:00
|
|
|
{
|
2015-12-16 17:17:38 +01:00
|
|
|
iname = data["iname"].data();
|
|
|
|
|
|
|
|
|
|
GdbMi wname = data["wname"];
|
|
|
|
|
if (wname.isValid()) // Happens (only) for watched expressions.
|
|
|
|
|
name = QString::fromUtf8(QByteArray::fromHex(wname.data()));
|
|
|
|
|
else
|
|
|
|
|
name = QString::fromLatin1(data["name"].data());
|
|
|
|
|
|
|
|
|
|
parseHelper(data);
|
2015-01-28 10:20:01 +01:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
if (wname.isValid())
|
|
|
|
|
exp = name.toUtf8();
|
2015-01-28 10:20:01 +01:00
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
WatchItem *WatchItem::parentItem() const
|
2015-04-01 17:19:43 +02:00
|
|
|
{
|
2015-12-16 17:17:38 +01:00
|
|
|
return static_cast<WatchItem *>(parent());
|
2015-04-01 17:19:43 +02:00
|
|
|
}
|
|
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
// Format a tooltip row with aligned colon.
|
|
|
|
|
static void formatToolTipRow(QTextStream &str, const QString &category, const QString &value)
|
2015-12-11 13:28:21 +01:00
|
|
|
{
|
2015-12-16 17:17:38 +01:00
|
|
|
QString val = value.toHtmlEscaped();
|
|
|
|
|
val.replace(QLatin1Char('\n'), QLatin1String("<br>"));
|
|
|
|
|
str << "<tr><td>" << category << "</td><td>";
|
|
|
|
|
if (!category.isEmpty())
|
|
|
|
|
str << ':';
|
|
|
|
|
str << "</td><td>" << val << "</td></tr>";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString WatchItem::toToolTip() const
|
|
|
|
|
{
|
|
|
|
|
QString res;
|
|
|
|
|
QTextStream str(&res);
|
|
|
|
|
str << "<html><body><table>";
|
|
|
|
|
formatToolTipRow(str, tr("Name"), name);
|
|
|
|
|
formatToolTipRow(str, tr("Expression"), expression());
|
|
|
|
|
formatToolTipRow(str, tr("Internal Type"), QLatin1String(type));
|
|
|
|
|
bool ok;
|
|
|
|
|
const quint64 intValue = value.toULongLong(&ok);
|
|
|
|
|
if (ok && intValue) {
|
|
|
|
|
formatToolTipRow(str, tr("Value"), QLatin1String("(dec) ") + value);
|
|
|
|
|
formatToolTipRow(str, QString(), QLatin1String("(hex) ") + QString::number(intValue, 16));
|
|
|
|
|
formatToolTipRow(str, QString(), QLatin1String("(oct) ") + QString::number(intValue, 8));
|
|
|
|
|
formatToolTipRow(str, QString(), QLatin1String("(bin) ") + QString::number(intValue, 2));
|
|
|
|
|
} else {
|
|
|
|
|
QString val = value;
|
|
|
|
|
if (val.size() > 1000) {
|
|
|
|
|
val.truncate(1000);
|
|
|
|
|
val += QLatin1Char(' ');
|
|
|
|
|
val += tr("... <cut off>");
|
|
|
|
|
}
|
|
|
|
|
formatToolTipRow(str, tr("Value"), val);
|
2015-04-01 17:19:43 +02:00
|
|
|
}
|
2015-12-16 17:17:38 +01:00
|
|
|
if (realAddress())
|
|
|
|
|
formatToolTipRow(str, tr("Object Address"), formatToolTipAddress(realAddress()));
|
|
|
|
|
if (origaddr)
|
|
|
|
|
formatToolTipRow(str, tr("Pointer Address"), formatToolTipAddress(origaddr));
|
|
|
|
|
if (arrayIndex >= 0)
|
|
|
|
|
formatToolTipRow(str, tr("Array Index"), QString::number(arrayIndex));
|
|
|
|
|
if (size)
|
|
|
|
|
formatToolTipRow(str, tr("Static Object Size"), tr("%n bytes", 0, size));
|
|
|
|
|
formatToolTipRow(str, tr("Internal ID"), QLatin1String(internalName()));
|
|
|
|
|
str << "</table></body></html>";
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool WatchItem::isLocal() const
|
|
|
|
|
{
|
|
|
|
|
if (arrayIndex >= 0)
|
|
|
|
|
if (const WatchItem *p = parentItem())
|
|
|
|
|
return p->isLocal();
|
|
|
|
|
return iname.startsWith("local.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool WatchItem::isWatcher() const
|
|
|
|
|
{
|
|
|
|
|
if (arrayIndex >= 0)
|
|
|
|
|
if (const WatchItem *p = parentItem())
|
|
|
|
|
return p->isWatcher();
|
|
|
|
|
return iname.startsWith("watch.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool WatchItem::isInspect() const
|
|
|
|
|
{
|
|
|
|
|
if (arrayIndex >= 0)
|
|
|
|
|
if (const WatchItem *p = parentItem())
|
|
|
|
|
return p->isInspect();
|
|
|
|
|
return iname.startsWith("inspect.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
quint64 WatchItem::realAddress() const
|
|
|
|
|
{
|
|
|
|
|
if (arrayIndex >= 0) {
|
|
|
|
|
if (const WatchItem *p = parentItem())
|
|
|
|
|
return p->address + arrayIndex * size;
|
|
|
|
|
}
|
|
|
|
|
return address;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray WatchItem::internalName() const
|
|
|
|
|
{
|
|
|
|
|
if (arrayIndex >= 0) {
|
|
|
|
|
if (const WatchItem *p = parentItem())
|
|
|
|
|
return p->iname + '.' + QByteArray::number(arrayIndex);
|
|
|
|
|
}
|
|
|
|
|
return iname;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString WatchItem::realName() const
|
|
|
|
|
{
|
|
|
|
|
if (arrayIndex >= 0)
|
|
|
|
|
return QString::fromLatin1("[%1]").arg(arrayIndex);
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString WatchItem::expression() const
|
|
|
|
|
{
|
|
|
|
|
if (!exp.isEmpty())
|
|
|
|
|
return QString::fromLatin1(exp);
|
|
|
|
|
if (quint64 addr = realAddress()) {
|
|
|
|
|
if (!type.isEmpty())
|
|
|
|
|
return QString::fromLatin1("*(%1*)0x%2").arg(QLatin1String(type)).arg(addr, 0, 16);
|
|
|
|
|
}
|
|
|
|
|
const WatchItem *p = parentItem();
|
|
|
|
|
if (p && !p->exp.isEmpty())
|
|
|
|
|
return QString::fromLatin1("(%1).%2").arg(QString::fromLatin1(p->exp), name);
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WatchItem *WatchItem::findItem(const QByteArray &iname)
|
|
|
|
|
{
|
|
|
|
|
if (internalName() == iname)
|
|
|
|
|
return this;
|
|
|
|
|
foreach (TreeItem *child, children()) {
|
|
|
|
|
auto witem = static_cast<WatchItem *>(child);
|
|
|
|
|
if (WatchItem *result = witem->findItem(iname))
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2015-04-01 17:19:43 +02:00
|
|
|
}
|
|
|
|
|
|
2010-04-26 18:54:08 +02:00
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Debugger
|
|
|
|
|
|