Commit c8a61cc8 authored by hjk's avatar hjk
Browse files

debugger: implement selected of string encoding per pointer type/individual pointer

parent 8575e4c7
......@@ -432,12 +432,50 @@ def qtNamespace():
# Happens for none-Qt applications
return ""
def encodeCharArray(p, size):
def findFirstZero(p, max):
for i in xrange(max):
if p.dereference() == 0:
return i
p = p + 1
return -1
def encodeCharArray(p, maxsize):
t = gdb.lookup_type("unsigned char").pointer()
p = p.cast(t)
i = findFirstZero(p, maxsize)
limit = select(i < 0, maxsize, i)
s = ""
p = p.cast(gdb.lookup_type("unsigned char").pointer())
for i in xrange(size):
for i in xrange(limit):
s += "%02x" % int(p.dereference())
p += 1
if i == maxsize:
s += "2e2e2e"
return s
def encodeChar2Array(p, maxsize):
t = gdb.lookup_type("unsigned short").pointer()
p = p.cast(t)
i = findFirstZero(p, maxsize)
limit = select(i < 0, maxsize, i)
s = ""
for i in xrange(limit):
s += "%04x" % int(p.dereference())
p += 1
if i == maxsize:
s += "2e002e002e00"
return s
def encodeChar4Array(p, maxsize):
t = gdb.lookup_type("unsigned int").pointer()
p = p.cast(t)
i = findFirstZero(p, maxsize)
limit = select(i < 0, maxsize, i)
s = ""
for i in xrange(limit):
s += "%08x" % int(p.dereference())
p += 1
if i == maxsize:
s += "2e0000002e0000002e000000"
return s
def encodeByteArray(value):
......@@ -450,10 +488,7 @@ def encodeByteArray(value):
if size > 0:
checkAccess(data, 4)
checkAccess(data + size) == 0
innerType = gdb.lookup_type("char")
p = gdb.Value(data.cast(innerType.pointer()))
return encodeCharArray(p, size)
return encodeCharArray(data, size)
def encodeString(value):
d_ptr = value['d'].dereference()
......@@ -502,22 +537,33 @@ class FrameCommand(gdb.Command):
def __init__(self):
super(FrameCommand, self).__init__("bb", gdb.COMMAND_OBSCURE)
def invoke(self, arg, from_tty):
args = arg.split(' ')
#warn("ARG: %s" % arg)
#warn("ARGS: %s" % args)
options = args[0].split(",")
varList = args[1][1:]
if len(varList) == 0:
varList = []
else:
varList = varList.split(",")
expandedINames = set(args[2].split(","))
def invoke(self, args, from_tty):
options = []
varList = []
typeformats = {}
formats = {}
watchers = ""
if len(args) > 3:
watchers = base64.b16decode(args[3], True)
#warn("WATCHERS: %s" % watchers)
for arg in args.split(' '):
pos = arg.find(":") + 1
if arg.startswith("options:"):
options = arg[pos:].split(",")
elif arg.startswith("vars:"):
vars = arg[pos:].split(",")
elif arg.startswith("expanded:"):
expandedINames = set(arg[pos:].split(","))
elif arg.startswith("typeformats:"):
for f in arg[pos:].split(","):
pos = f.find("=")
if pos != -1:
type = base64.b16decode(f[0:pos], True)
typeformats[type] = int(f[pos+1:])
elif arg.startswith("formats:"):
for f in arg[pos:].split(","):
pos = f.find("=")
if pos != -1:
formats[f[0:pos]] = int(f[pos+1:])
elif arg.startswith("watchers:"):
watchers = base64.b16decode(arg[pos:], True)
useFancy = "fancy" in options
......@@ -552,6 +598,8 @@ class FrameCommand(gdb.Command):
d = Dumper()
d.dumpers = self.dumpers
d.typeformats = typeformats
d.formats = formats
d.useFancy = useFancy
d.passExceptions = "passexceptions" in options
d.autoDerefPointers = "autoderef" in options
......@@ -837,6 +885,9 @@ class Dumper:
if len(type) > 0 and type != self.childTypes[-1]:
self.put('type="%s",' % type) # str(type.unqualified()) ?
def putAddress(self, addr):
self.put('addr="%s",' % cleanAddress(addr))
def putNumChild(self, numchild):
#warn("NUM CHILD: '%s' '%s'" % (numchild, self.childNumChilds[-1]))
if numchild != self.childNumChilds[-1]:
......@@ -1038,13 +1089,36 @@ class Dumper:
elif type.code == gdb.TYPE_CODE_PTR:
#warn("A POINTER: %s" % value.type)
isHandled = False
if str(type.strip_typedefs()).find("(") != -1:
format = self.formats.get(item.iname)
if format is None:
format = self.typeformats.get(str(value.type))
if not format is None:
self.putAddress(value.address)
self.putType(item.value.type)
self.putNumChild(0)
isHandled = True
if format == 0:
# Bald pointer.
self.putValue(str(cleanAddress(value.address)))
elif format == 1 or format == 2:
# Latin1 or UTF-8
f = select(format == 1, "6", "9")
self.putValue(encodeCharArray(value, 100), f)
elif format == 3:
# UTF-16.
self.putValue(encodeChar2Array(value, 100), "11")
elif format == 4:
# UCS-4:
self.putValue(encodeChar4Array(value, 100), "10")
if (not isHandled) and str(type.strip_typedefs()).find("(") != -1:
# A function pointer.
self.putValue(str(item.value))
self.put('addr="%s",' % cleanAddress(value.address))
self.putAddress(value.address)
self.putType(item.value.type)
self.putNumChild(0)
isHandled = True
......@@ -1069,21 +1143,8 @@ class Dumper:
# Display values up to given length directly
#warn("CHAR AUTODEREF: %s" % value.address)
self.putType(item.value.type)
firstNul = -1
p = value
found = False
for i in xrange(100):
if p.dereference() == 0:
# Found terminating NUL
found = True
break
p += 1
if found:
self.putValue(encodeCharArray(value, i), "6")
self.putNumChild(0)
else:
self.putValue(encodeCharArray(value, 100) + "2e2e2e", "6")
self.putNumChild(0)
self.putValue(encodeCharArray(value, 100), "6")
self.putNumChild(0)
isHandled = True
#warn("AUTODEREF: %s" % self.autoDerefPointers)
......@@ -1105,8 +1166,9 @@ class Dumper:
if not isHandled:
#warn("GENERIC PLAIN POINTER: %s" % value.type)
self.putType(item.value.type)
self.putValue(str(value))
self.put('addr="%s",' % cleanAddress(value.address))
#self.putValue(str(value))
self.putValue("")
self.putAddress(value.address)
self.putNumChild(1)
if self.isExpanded(item):
self.beginChildren()
......@@ -1208,7 +1270,7 @@ class Dumper:
value = item.value[field.name]
child = Item(value, item.iname, field.name, field.name)
self.beginHash()
self.put('addr="%s",' % cleanAddress(value.address))
self.putAddress(value.address)
self.putItemHelper(child)
self.endHash();
else:
......
......@@ -252,10 +252,10 @@ void GdbEngine::updateSubItemClassic(const WatchData &data0)
return;
}
// we should have a type now. this is relied upon further below
// We should have a type now. This is relied upon further below.
QTC_ASSERT(!data.type.isEmpty(), return);
// a common case that can be easily solved
// A common case that can be easily solved.
if (data.isChildrenNeeded() && isPointerType(data.type)
&& !hasDebuggingHelperForType(data.type)) {
// We sometimes know what kind of children pointers have
......@@ -431,8 +431,6 @@ void GdbEngine::handleDebuggingHelperValue3Classic(const GdbResponse &response)
while (out.endsWith(' ') || out.endsWith('\n'))
out.chop(1);
QList<QByteArray> list = out.split(' ');
//qDebug() << "RECEIVED" << response.toString() << "FOR" << data0.toString()
// << " STREAM:" << out;
if (list.isEmpty()) {
//: Value for variable
data.setError(WatchData::msgNotInScope());
......@@ -538,7 +536,7 @@ void GdbEngine::tryLoadDebuggingHelpersClassic()
postCommand("call (void*)dlopen(\"" + GdbMi::escapeCString(dlopenLib)
+ "\", " + flag + ")",
CB(handleDebuggingHelperSetup));
// some older systems like CentOS 4.6 prefer this:
// Some older systems like CentOS 4.6 prefer this:
postCommand("call (void*)__dlopen(\"" + GdbMi::escapeCString(dlopenLib)
+ "\", " + flag + ")",
CB(handleDebuggingHelperSetup));
......@@ -550,7 +548,7 @@ void GdbEngine::tryLoadDebuggingHelpersClassic()
void GdbEngine::tryQueryDebuggingHelpersClassic()
{
PRECONDITION;
// retrieve list of dumpable classes
// Retrieve list of dumpable classes.
postCommand("call (void*)qDumpObjectData440(1,0,0,0,0,0,0,0)");
postCommand("p (char*)&qDumpOutBuffer",
CB(handleQueryDebuggingHelperClassic));
......@@ -560,7 +558,7 @@ void GdbEngine::recheckDebuggingHelperAvailabilityClassic()
{
PRECONDITION;
if (m_gdbAdapter->dumperHandling() != AbstractGdbAdapter::DumperNotAvailable) {
// retrieve list of dumpable classes
// Retrieve list of dumpable classes.
postCommand("call (void*)qDumpObjectData440(1,0,0,0,0,0,0,0)");
postCommand("p (char*)&qDumpOutBuffer",
CB(handleQueryDebuggingHelperClassic));
......@@ -681,7 +679,6 @@ bool GdbEngine::checkDebuggingHelpersClassic()
if (!manager()->qtDumperLibraryEnabled())
return false;
const QString lib = qtDumperLibraryName();
//qDebug() << "DUMPERLIB:" << lib;
const QFileInfo fi(lib);
if (!fi.exists()) {
const QStringList &locations = manager()->qtDumperLibraryLocations();
......
......@@ -3321,7 +3321,7 @@ void GdbEngine::setWatchDataDisplayedType(WatchData &data, const GdbMi &item)
void GdbEngine::handleVarCreate(const GdbResponse &response)
{
WatchData data = response.cookie.value<WatchData>();
// happens e.g. when we already issued a var-evaluate command
// Happens e.g. when we already issued a var-evaluate command.
if (!data.isValid())
return;
//qDebug() << "HANDLE VARIABLE CREATION:" << data.toString();
......@@ -3351,11 +3351,9 @@ void GdbEngine::handleVarCreate(const GdbResponse &response)
void GdbEngine::handleDebuggingHelperSetup(const GdbResponse &response)
{
//qDebug() << "CUSTOM SETUP RESULT:" << response.toString();
if (response.resultClass == GdbResultDone) {
} else {
QString msg = QString::fromLocal8Bit(response.data.findChild("msg").data());
//qDebug() << "CUSTOM DUMPER SETUP ERROR MESSAGE:" << msg;
showStatusMessage(tr("Custom dumper setup: %1").arg(msg), 10000);
}
}
......@@ -3381,7 +3379,6 @@ void GdbEngine::handleChildren(const WatchData &data0, const GdbMi &item,
}
setWatchDataType(data, item.findChild("type"));
setWatchDataEditValue(data, item.findChild("editvalue"));
setWatchDataChildCount(data, item.findChild("numchild"));
setWatchDataValue(data, item.findChild("value"),
item.findChild("valueencoded").data().toInt());
setWatchDataAddress(data, item.findChild("addr"));
......@@ -3391,6 +3388,7 @@ void GdbEngine::handleChildren(const WatchData &data0, const GdbMi &item,
item.findChild("valuetooltipencoded").data().toInt());
setWatchDataValueEnabled(data, item.findChild("valueenabled"));
setWatchDataValueEditable(data, item.findChild("valueeditable"));
setWatchDataChildCount(data, item.findChild("numchild"));
//qDebug() << "\nAPPEND TO LIST: " << data.toString() << "\n";
list->append(data);
......@@ -3451,7 +3449,7 @@ void GdbEngine::updateLocals(const QVariant &cookie)
}
// Parse a local variable from GdbMi
// Parse a local variable from GdbMi.
WatchData GdbEngine::localVariable(const GdbMi &item,
const QStringList &uninitializedVariables,
QMap<QByteArray, int> *seen)
......@@ -3479,7 +3477,7 @@ WatchData GdbEngine::localVariable(const GdbMi &item,
WatchData data;
QString nam = _(name);
data.iname = "local." + name + QByteArray::number(n + 1);
//: Variable %1 is the variable name, %2 is a simple count
//: Variable %1 is the variable name, %2 is a simple count.
data.name = WatchData::shadowedName(nam, n);
if (uninitializedVariables.contains(data.name)) {
data.setError(WatchData::msgNotInScope());
......@@ -3511,8 +3509,8 @@ WatchData GdbEngine::localVariable(const GdbMi &item,
// somewhere in the response.
data.setChildrenUnneeded();
} else {
// set value only directly if it is simple enough, otherwise
// pass through the insertData() machinery
// Set value only directly if it is simple enough, otherwise
// pass through the insertData() machinery.
if (isIntOrFloatType(data.type) || isPointerType(data.type))
setWatchDataValue(data, item.findChild("value"));
if (isSymbianIntType(data.type)) {
......@@ -3523,7 +3521,11 @@ WatchData GdbEngine::localVariable(const GdbMi &item,
if (!m_manager->watchHandler()->isExpandedIName(data.iname))
data.setChildrenUnneeded();
if (isPointerType(data.type) || data.name == __("this"))
GdbMi t = item.findChild("numchild");
if (t.isValid())
data.setHasChildren(t.data().toInt() > 0);
else if (isPointerType(data.type) || data.name == __("this"))
data.setHasChildren(true);
return data;
}
......
......@@ -55,13 +55,7 @@ void GdbEngine::updateLocalsPython(const QByteArray &varList)
//m_toolTipExpression.clear();
WatchHandler *handler = m_manager->watchHandler();
QByteArray expanded;
QSet<QByteArray> expandedINames = handler->expandedINames();
QSetIterator<QByteArray> jt(expandedINames);
while (jt.hasNext()) {
expanded.append(jt.next());
expanded.append(',');
}
QByteArray expanded = handler->formatRequests();
if (expanded.isEmpty())
expanded.append("defaults,");
expanded.chop(1);
......@@ -92,8 +86,8 @@ void GdbEngine::updateLocalsPython(const QByteArray &varList)
options += "defaults,";
options.chop(1);
postCommand("bb " + options + " @" + varList + ' '
+ expanded + ' ' + watchers.toHex(),
postCommand("bb options:" + options + " vars:" + varList + ' '
+ expanded + " watchers:" + watchers.toHex(),
WatchUpdate, CB(handleStackFramePython));
}
......
......@@ -639,27 +639,6 @@ static QString formattedValue(const WatchData &data, int format)
return reformatInteger(data.value.toULongLong(), format);
return reformatInteger(data.value.toLongLong(), format);
}
if (0 && !data.addr.isEmpty()) {
if (format == BaldPointerFormat)
return data.value;
bool ok = false;
const void *addr =
reinterpret_cast<void *>(data.value.toULongLong(&ok, 0));
if (!ok || !addr)
return data.value;
// FIXME: add a round trip through the debugger to prevent crashs?
if (format == Latin1StringFormat)
return QString::fromLatin1(static_cast<const char *>(addr));
if (format == Local8BitStringFormat)
return QString::fromLocal8Bit(static_cast<const char *>(addr));
if (format == Utf8StringFormat)
return QString::fromUtf8(static_cast<const char *>(addr));
if (format == Utf16StringFormat)
return QString::fromUtf16(static_cast<const ushort *>(addr));
if (format == Ucs4StringFormat)
return QString::fromUcs4(static_cast<const uint *>(addr));
return data.value;
}
return data.value;
}
......@@ -802,7 +781,6 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
int format = m_handler->m_individualFormats.value(data.iname, -1);
if (format == -1)
format = m_handler->m_typeFormats.value(data.type, -1);
//qDebug() << "FORMATTED: " << format << formattedValue(data, format);
return truncateValue(formattedValue(data, format));
}
case 2: {
......@@ -845,7 +823,7 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
if (isIntType(data.type))
return QStringList() << tr("decimal") << tr("hexadecimal")
<< tr("binary") << tr("octal");
if (!data.addr.isEmpty())
if (data.type.endsWith(QLatin1Char('*')))
return QStringList()
<< tr("Bald pointer")
<< tr("Latin1 string")
......@@ -889,6 +867,7 @@ bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int ro
}
} else if (role == TypeFormatRole) {
m_handler->setFormat(data.type, value.toInt());
m_handler->m_manager->updateWatchData(data);
} else if (role == IndividualFormatRole) {
const int format = value.toInt();
if (format == -1) {
......@@ -896,6 +875,7 @@ bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int ro
} else {
m_handler->m_individualFormats[data.iname] = format;
}
m_handler->m_manager->updateWatchData(data);
}
emit dataChanged(index, index);
return true;
......@@ -1155,8 +1135,8 @@ WatchItem *WatchModel::findItem(const QByteArray &iname, WatchItem *root) const
static void debugRecursion(QDebug &d, const WatchItem *item, int depth)
{
d << QString(2 * depth, QLatin1Char(' ')) << item->toString() << '\n';
foreach(const WatchItem *i, item->children)
debugRecursion(d, i, depth + 1);
foreach (const WatchItem *child, item->children)
debugRecursion(d, child, depth + 1);
}
QDebug operator<<(QDebug d, const WatchModel &m)
......@@ -1167,6 +1147,16 @@ QDebug operator<<(QDebug d, const WatchModel &m)
return d;
}
void WatchModel::formatRequests(QByteArray *out, const WatchItem *item) const
{
int format = m_handler->m_individualFormats.value(item->iname, -1);
if (format == -1)
format = m_handler->m_typeFormats.value(item->type, -1);
if (format != -1)
*out += item->iname + ":format=" + QByteArray::number(format) + ',';
foreach (const WatchItem *child, item->children)
formatRequests(out, child);
}
///////////////////////////////////////////////////////////////////////
//
......@@ -1573,5 +1563,65 @@ void WatchHandler::setFormat(const QString &type, int format)
m_tooltips->emitDataChanged(1);
}
int WatchHandler::format(const QByteArray &iname) const
{
int result = -1;
if (const WatchData *item = findItem(iname)) {
int result = m_individualFormats.value(iname, -1);
if (result == -1)
result = m_typeFormats.value(item->type, -1);
}
return result;
}
QByteArray WatchHandler::formatRequests() const
{
QByteArray ba;
//m_locals->formatRequests(&ba, m_locals->m_root);
//m_watchers->formatRequests(&ba, m_watchers->m_root);
ba.append("expanded:");
if (!m_expandedINames.isEmpty()) {
QSetIterator<QByteArray> jt(m_expandedINames);
while (jt.hasNext()) {
QByteArray iname = jt.next();
ba.append(iname);
ba.append(',');
}
ba.chop(1);
}
ba.append(' ');
ba.append("typeformats:");
if (!m_typeFormats.isEmpty()) {
QHashIterator<QString, int> it(m_typeFormats);
while (it.hasNext()) {
it.next();
ba.append(it.key().toLatin1().toHex());
ba.append('=');
ba.append(QByteArray::number(it.value()));
ba.append(',');
}
ba.chop(1);
}
ba.append(' ');
ba.append("formats:");
if (!m_individualFormats.isEmpty()) {
QHashIterator<QString, int> it(m_individualFormats);
while (it.hasNext()) {
it.next();
ba.append(it.key().toLatin1());
ba.append('=');
ba.append(QByteArray::number(it.value()));
ba.append(',');
}
ba.chop(1);
}
ba.append(' ');
return ba;
}
} // namespace Internal
} // namespace Debugger
......@@ -139,8 +139,6 @@ public:
bool valueEditable; // value will be editable
bool error;
private:
public:
int source; // Used by some debuggers (CDB) to tell where it originates from (dumper or symbol evaluation)
int state;
......@@ -239,6 +237,7 @@ signals:
private:
QString niceType(const QString &typeIn) const;
void formatRequests(QByteArray *out, const WatchItem *item) const;
WatchHandler *m_handler;
WatchType m_type;
......@@ -284,8 +283,10 @@ public:
QStringList watchedExpressions() const;
QHash<QByteArray, int> watcherNames() const
{ return m_watcherNames; }
QByteArray formatRequests() const;
static QString watcherEditPlaceHolder();
int format(const QByteArray &iname) const;
private:
friend class WatchModel;
......
......@@ -640,7 +640,7 @@ QString decodeData(const QByteArray &ba, int encoding)
case 5: { // base64 encoded 8 bit data, without quotes (see 1)
return quoteUnprintableLatin1(QByteArray::fromBase64(ba));
}
case 6: { // %02x encoded 8 bit data
case 6: { // %02x encoded 8 bit Latin1 data
const QChar doubleQuote(QLatin1Char('"'));
const QByteArray decodedBa = QByteArray::fromHex(ba);
//qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
......@@ -660,6 +660,39 @@ QString decodeData(const QByteArray &ba, int encoding)
return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
(decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
}
case 9: { // %02x encoded 8 bit Utf-8 data
const QChar doubleQuote(QLatin1Char('"'));
const QByteArray decodedBa = QByteArray::fromHex(ba);
//qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
return doubleQuote + QString::fromUtf8(decodedBa) + doubleQuote;
}
case 10: { // %08x encoded 32 bit data, Big Endian
const QChar doubleQuote(QLatin1Char('"'));
QByteArray decodedBa = QByteArray::fromHex(ba);
for (int i = 0; i < decodedBa.size(); i += 4) {
char c = decodedBa.at(i);
decodedBa[i] = decodedBa.at(i + 3);
decodedBa[i + 3] = c;
c = decodedBa.at(i + 1);
decodedBa[i + 1] = decodedBa.at(i + 2);
decodedBa[i + 2] = c;
}
//qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
(decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
}
case 11: { // %02x encoded 16 bit data, Big Endian
const QChar doubleQuote(QLatin1Char('"'));
QByteArray decodedBa = QByteArray::fromHex(ba);
for (int i = 0; i < decodedBa.size(); i += 2) {
char c = decodedBa.at(i);
decodedBa[i] = decodedBa.at(i + 1);