Commit d982b216 authored by hjk's avatar hjk Committed by hjk
Browse files

debugger: rework dumping arrays of plain data



Simple array data can be fetched in one go.
The "normal" way gdb asks the server for contents is one item
at a time, at ~3ms per round trip. Now we get 2000 in 200ms.

This also introduces a re-usable readRawMemory function and
removes a few checkAccess calls which will be triggered anyways.

Change-Id: Ic07a3d6593fd2ea45f7a8058509118fe22a845bb
Reviewed-by: default avatarhjk <qthjk@ovi.com>
parent 6f5e48be
import binascii
cdbLoaded = False
lldbLoaded = False
gdbLoaded = False
......@@ -287,6 +289,29 @@ try:
removeTempFile(filename, file)
return lines
def selectedInferior():
try:
# Does not exist in 7.3.
return gdb.selected_inferior()
except:
pass
# gdb.Inferior is new in gdb 7.2
return gdb.inferiors()[0]
def readRawMemory(base, size):
try:
inferior = selectedInferior()
return binascii.hexlify(inferior.read_memory(base, size))
except:
pass
s = ""
t = lookupType("unsigned char").pointer()
base = base.cast(t)
for i in xrange(size):
s += "%02x" % int(base.dereference())
base += 1
return s
#######################################################################
#
# Types
......
......@@ -42,11 +42,6 @@ def removeTempFile(name, file):
except:
pass
try:
import binascii
except:
pass
verbosity = 0
verbosity = 1
......@@ -69,8 +64,14 @@ Hex4EncodedLittleEndianWithoutQuotes, \
Hex2EncodedLocal8Bit, \
JulianDate, \
MillisecondsSinceMidnight, \
JulianDateAndMillisecondsSinceMidnight \
= range(17)
JulianDateAndMillisecondsSinceMidnight, \
Hex2EncodedInt1, \
Hex2EncodedInt2, \
Hex2EncodedInt4, \
Hex2EncodedInt8, \
Hex2EncodedFloat4, \
Hex2EncodedFloat8 \
= range(23)
# Display modes
StopDisplay, \
......@@ -479,6 +480,26 @@ def isSimpleType(typeobj):
or code == FloatCode \
or code == EnumCode
def simpleEncoding(typeobj):
code = typeobj.code
if code == BoolCode or code == CharCode:
return Hex2EncodedInt1
if code == IntCode:
if typeobj.sizeof == 1:
return Hex2EncodedInt1
if typeobj.sizeof == 2:
return Hex2EncodedInt2
if typeobj.sizeof == 4:
return Hex2EncodedInt4
if typeobj.sizeof == 8:
return Hex2EncodedInt8
if code == FloatCode:
if typeobj.sizeof == 4:
return Hex2EncodedFloat4
if typeobj.sizeof == 8:
return Hex2EncodedFloat8
return None
def warn(message):
if True or verbosity > 0:
print "XXX: %s\n" % message.encode("latin1")
......@@ -668,80 +689,23 @@ def findFirstZero(p, maximum):
p = p + 1
return maximum + 1
def extractCharArray(p, maxsize):
p = p.cast(lookupType("unsigned char").pointer())
s = ""
i = 0
while i < maxsize:
c = int(p.dereference())
if c == 0:
return s
s += "%c" % c
p += 1
i += 1
if p.dereference() != 0:
s += "..."
def encodeCArray(p, innerType, suffix):
t = lookupType(innerType)
p = p.cast(t.pointer())
limit = findFirstZero(p, qqStringCutOff)
s = readRawMemory(p, limit * t.sizeof)
if limit > qqStringCutOff:
s += suffix
return s
def extractByteArray(value):
d_ptr = value['d'].dereference()
data = d_ptr['data']
size = d_ptr['size']
alloc = d_ptr['alloc']
check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
checkRef(d_ptr["ref"])
if size > 0:
checkAccess(data, 4)
checkAccess(data + size) == 0
return extractCharArray(data, min(qqStringCutOff, size))
def encodeCharArray(p, maxsize = None, limit = None):
if maxsize is None:
maxsize = qqStringCutOff
t = lookupType("unsigned char").pointer()
p = p.cast(t)
if limit is None:
limit = findFirstZero(p, maxsize)
s = ""
try:
# gdb.Inferior is new in gdb 7.2
inferior = gdb.inferiors()[0]
s = binascii.hexlify(inferior.read_memory(p, limit))
except:
for i in xrange(limit):
s += "%02x" % int(p.dereference())
p += 1
if limit > maxsize:
s += "2e2e2e"
return s
def encodeCharArray(p):
return encodeCArray(p, "unsigned char", "2e2e2e")
def encodeChar2Array(p, maxsize = None):
if maxsize == None:
maxsize = qqStringCutOff
t = lookupType("unsigned short").pointer()
p = p.cast(t)
limit = findFirstZero(p, maxsize)
s = ""
for i in xrange(limit):
s += "%04x" % int(p.dereference())
p += 1
if i == maxsize:
s += "2e002e002e00"
return s
def encodeChar2Array(p):
return encodeCArray(p, "unsigned short", "2e002e002e00")
def encodeChar4Array(p, maxsize = None):
if maxsize == None:
maxsize = qqStringCutOff
t = lookupType("unsigned int").pointer()
p = p.cast(t)
limit = findFirstZero(p, maxsize)
s = ""
for i in xrange(limit):
s += "%08x" % int(p.dereference())
p += 1
if i > maxsize:
s += "2e0000002e0000002e000000"
return s
def encodeChar4Array(p):
return encodeCArray(p, "unsigned int", "2e0000002e0000002e000000")
def qByteArrayData(value):
private = value['d']
......@@ -758,13 +722,15 @@ def qByteArrayData(value):
def encodeByteArray(value):
data, size, alloc = qByteArrayData(value)
check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
if size > 0:
checkAccess(data, 4)
checkAccess(data + size) == 0
return encodeCharArray(data, limit = size)
if alloc != 0:
check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
limit = min(size, qqStringCutOff)
s = readRawMemory(data, limit)
if limit < size:
s += "2e2e2e"
return s
def qQStringData(value):
def qStringData(value):
private = value['d']
checkRef(private['ref'])
try:
......@@ -778,26 +744,11 @@ def qQStringData(value):
return private['data'], int(private['size']), int(private['alloc'])
def encodeString(value):
data, size, alloc = qQStringData(value)
data, size, alloc = qStringData(value)
if alloc != 0:
check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
if size > 0:
checkAccess(data, 4)
checkAccess(data + size) == 0
s = ""
limit = min(size, qqStringCutOff)
try:
# gdb.Inferior is new in gdb 7.2
inferior = gdb.inferiors()[0]
s = binascii.hexlify(inferior.read_memory(data, 2 * limit))
except:
p = data
for i in xrange(limit):
val = int(p.dereference())
s += "%02x" % (val % 256)
s += "%02x" % (val / 256)
p += 1
s = readRawMemory(data, 2 * limit)
if limit < size:
s += "2e002e002e00"
return s
......@@ -1082,7 +1033,7 @@ class Dumper:
for item in listOfLocals([]):
self.expandedINames.add(item.iname)
self.expandedINames.discard("")
warn("EXPANDED: %s" % self.expandedINames)
#warn("EXPANDED: %s" % self.expandedINames)
# Take care of the return value of the last function call.
if len(resultVarName) > 0:
......@@ -1353,21 +1304,25 @@ class Dumper:
self.putName(name)
self.putItem(value)
def tryPutArrayContents(self, type, base, n):
if isSimpleType(type):
self.put('{value="')
self.put('"},{value="'.join([str((base + i).dereference())
for i in xrange(n)]))
self.put('"}');
return True
return False
def tryPutArrayContents(self, typeobj, base, n):
if not isSimpleType(typeobj):
return False
size = n * typeobj.sizeof;
self.put('childtype="%s",' % typeobj)
self.put('addrbase="0x%x",' % long(base))
self.put('addrstep="0x%x",' % long(typeobj.sizeof))
self.put('arrayencoding="%s",' % simpleEncoding(typeobj))
self.put('arraydata="')
self.put(readRawMemory(base, size))
self.put('",')
return True
def putArrayData(self, type, base, n,
childNumChild = None, maxNumChild = 10000):
base = base.cast(type.pointer())
with Children(self, n, type, childNumChild, maxNumChild,
base, type.sizeof):
if not self.tryPutArrayContents(type, base, n):
if not self.tryPutArrayContents(type, base, n):
with Children(self, n, type, childNumChild, maxNumChild,
base, type.sizeof):
for i in self.childRange():
self.putSubItem(i, (base + i).dereference())
......@@ -1507,9 +1462,9 @@ class Dumper:
if self.currentIName in self.expandedINames:
p = value.cast(targetType.pointer())
ts = targetType.sizeof
with Children(self, childType=targetType,
addrBase=p, addrStep=ts):
if not self.tryPutArrayContents(targetType, p, type.sizeof/ts):
if not self.tryPutArrayContents(targetType, p, type.sizeof/ts):
with Children(self, childType=targetType,
addrBase=p, addrStep=ts):
self.putFields(value)
return
......@@ -1557,7 +1512,7 @@ class Dumper:
# Use Latin1 as default for char *.
self.putAddress(value.address)
self.putType(typeName)
self.putValue(encodeCharArray(value, 100), Hex2EncodedLatin1)
self.putValue(encodeCharArray(value), Hex2EncodedLatin1)
self.putNumChild(0)
return
......@@ -1578,7 +1533,7 @@ class Dumper:
# Explicitly requested Latin1 formatting.
self.putAddress(value.address)
self.putType(typeName)
self.putValue(encodeCharArray(value, 100), Hex2EncodedLatin1)
self.putValue(encodeCharArray(value), Hex2EncodedLatin1)
self.putNumChild(0)
return
......@@ -1586,7 +1541,7 @@ class Dumper:
# Explicitly requested UTF-8 formatting.
self.putAddress(value.address)
self.putType(typeName)
self.putValue(encodeCharArray(value, 100), Hex2EncodedUtf8)
self.putValue(encodeCharArray(value), Hex2EncodedUtf8)
self.putNumChild(0)
return
......@@ -1594,7 +1549,7 @@ class Dumper:
# Explicitly requested local 8 bit formatting.
self.putAddress(value.address)
self.putType(typeName)
self.putValue(encodeCharArray(value, 100), Hex2EncodedLocal8Bit)
self.putValue(encodeCharArray(value), Hex2EncodedLocal8Bit)
self.putNumChild(0)
return
......@@ -1602,7 +1557,7 @@ class Dumper:
# Explicitly requested UTF-16 formatting.
self.putAddress(value.address)
self.putType(typeName)
self.putValue(encodeChar2Array(value), Hex4EncodedBigEndian)
self.putValue(encodeChar2Array(value), Hex4EncodedLittleEndian)
self.putNumChild(0)
return
......@@ -1610,7 +1565,7 @@ class Dumper:
# Explicitly requested UCS-4 formatting.
self.putAddress(value.address)
self.putType(typeName)
self.putValue(encodeChar4Array(value), Hex8EncodedBigEndian)
self.putValue(encodeChar4Array(value), Hex8EncodedLittleEndian)
self.putNumChild(0)
return
......@@ -1860,7 +1815,8 @@ def threadnames(arg):
out = '['
oldthread = gdb.selected_thread()
try:
for thread in gdb.inferiors()[0].threads():
inferior = selectedInferior()
for thread in inferior.threads():
maximalStackDepth = int(arg)
thread.switch()
e = gdb.selected_frame ()
......
......@@ -459,9 +459,6 @@ def qdump__QList(d, value):
check(begin >= 0 and end >= 0 and end <= 1000 * 1000 * 1000)
size = end - begin
check(size >= 0)
#if n > 0:
# checkAccess(&list.front())
# checkAccess(&list.back())
checkRef(d_ptr["ref"])
# Additional checks on pointer arrays.
......@@ -1650,7 +1647,6 @@ def qdump__QVector(d, value):
d.putItemCount(size)
d.putNumChild(size)
if d.isExpanded():
d.putField("size", size)
d.putArrayData(innerType, p, size)
......@@ -2569,8 +2565,10 @@ if False:
def qdump__Function(d, value):
min = value["min"]
max = value["max"]
var = extractByteArray(value["var"])
f = extractByteArray(value["f"])
data, size, alloc = qByteArrayData(value["var"])
var = extractCString(data)
data, size, alloc = qByteArrayData(value["f"])
f = extractCString(data)
d.putValue("%s, %s=%f..%f" % (f, var, min, max))
d.putNumChild(0)
d.putField("typeformats", "Normal,Displayed");
......
......@@ -592,7 +592,7 @@ QString decodeData(const QByteArray &ba, int encoding)
case Hex8EncodedBigEndian: { // 10, %08x encoded 32 bit data
const QChar doubleQuote(QLatin1Char('"'));
QByteArray decodedBa = QByteArray::fromHex(ba);
for (int i = 0; i < decodedBa.size(); i += 4) {
for (int i = 0; i < decodedBa.size() - 3; i += 4) {
char c = decodedBa.at(i);
decodedBa[i] = decodedBa.at(i + 3);
decodedBa[i + 3] = c;
......@@ -643,6 +643,54 @@ QString decodeData(const QByteArray &ba, int encoding)
return QCoreApplication::translate("Debugger", "<Encoding error>");
}
template <class T>
void decodeArrayHelper(QList<WatchData> *list, const WatchData &tmplate,
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);
data.value = QString::number(p[i]);
data.address += i * sizeof(T);
data.exp = exp + QByteArray::number(data.address, 16);
data.setAllUnneeded();
list->append(data);
}
}
void decodeArray(QList<WatchData> *list, const WatchData &tmplate,
const QByteArray &rawData, int encoding)
{
switch (encoding) {
case Hex2EncodedInt1:
decodeArrayHelper<uchar>(list, tmplate, rawData);
break;
case Hex2EncodedInt2:
decodeArrayHelper<ushort>(list, tmplate, rawData);
break;
case Hex2EncodedInt4:
decodeArrayHelper<uint>(list, tmplate, rawData);
break;
case Hex2EncodedInt8:
decodeArrayHelper<quint64>(list, tmplate, rawData);
break;
case Hex2EncodedFloat4:
decodeArrayHelper<float>(list, tmplate, rawData);
break;
case Hex2EncodedFloat8:
decodeArrayHelper<double>(list, tmplate, rawData);
break;
default:
qDebug() << "ENCODING ERROR: " << encoding;
}
}
// Editor tooltip support
bool isCppEditor(Core::IEditor *editor)
{
......@@ -870,41 +918,49 @@ void parseWatchData(const QSet<QByteArray> &expandedINames,
setWatchDataChildCount(childtemplate, item.findChild("childnumchild"));
//qDebug() << "CHILD TEMPLATE:" << childtemplate.toString();
for (int i = 0, n = children.children().size(); i != n; ++i) {
const GdbMi &child = children.children().at(i);
WatchData data1 = childtemplate;
data1.sortId = i;
GdbMi name = child.findChild("name");
if (name.isValid())
data1.name = _(name.data());
else
data1.name = QString::number(i);
GdbMi iname = child.findChild("iname");
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;
}
QByteArray key = child.findChild("key").data();
if (!key.isEmpty()) {
int encoding = child.findChild("keyencoded").data().toInt();
QString skey = decodeData(key, encoding);
if (skey.size() > 13) {
skey = skey.left(12);
skey += _("...");
mi = item.findChild("arraydata");
if (mi.isValid()) {
int encoding = item.findChild("arrayencoding").data().toInt();
childtemplate.iname = data.iname + '.';
childtemplate.address = addressBase;
decodeArray(list, childtemplate, mi.data(), encoding);
} else {
for (int i = 0, n = children.children().size(); i != n; ++i) {
const GdbMi &child = children.children().at(i);
WatchData data1 = childtemplate;
data1.sortId = i;
GdbMi name = child.findChild("name");
if (name.isValid())
data1.name = _(name.data());
else
data1.name = QString::number(i);
GdbMi iname = child.findChild("iname");
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;
}
QByteArray key = child.findChild("key").data();
if (!key.isEmpty()) {
int encoding = child.findChild("keyencoded").data().toInt();
QString skey = decodeData(key, encoding);
if (skey.size() > 13) {
skey = skey.left(12);
skey += _("...");
}
//data1.name += " (" + skey + ")";
data1.name = skey;
}
//data1.name += " (" + skey + ")";
data1.name = skey;
parseWatchData(expandedINames, data1, child, list);
}
parseWatchData(expandedINames, data1, child, list);
}
}
......
......@@ -72,7 +72,13 @@ enum DebuggerEncoding
Hex2EncodedLocal8BitWithQuotes = 13,
JulianDate = 14,
MillisecondsSinceMidnight = 15,
JulianDateAndMillisecondsSinceMidnight = 16
JulianDateAndMillisecondsSinceMidnight = 16,
Hex2EncodedInt1 = 17,
Hex2EncodedInt2 = 18,
Hex2EncodedInt4 = 19,
Hex2EncodedInt8 = 20,
Hex2EncodedFloat4 = 21,
Hex2EncodedFloat8 = 22
};
bool isEditorDebuggable(Core::IEditor *editor);
......@@ -103,6 +109,9 @@ QString cppExpressionAt(TextEditor::ITextEditor *editor, int pos,
QString cppFunctionAt(const QString &fileName, int line);
// Decode string data as returned by the dumper helpers.
QString decodeData(const QByteArray &baIn, int encoding);
// Decode string data as returned by the dumper helpers.
void decodeArray(WatchData *list, const WatchData &tmplate,
const QByteArray &rawData, int encoding);
// Get variables that are not initialized at a certain line
// of a function from the code model. Shadowed variables will
......
......@@ -2714,7 +2714,7 @@ namespace stdmap {
void testStdMapUIntFloatIterator()
{
typedef std::map<uint, float> Map;
typedef std::map<int, float> Map;
Map map;
map[11] = 11.0;
map[22] = 22.0;
......@@ -2732,11 +2732,11 @@ namespace stdmap {
BREAK_HERE;
// Expand map.
// Check map <6 items> std::map<unsigned int, float>.
// Check map <6 items> std::map<int, float>.
// Check map.11 11 float.
// Check it1.first 11 unsigned int.
// Check it1.first 11 int.
// Check it1.second 11 float.
// Check it1.first 55 unsigned int.
// Check it1.first 55 int.
// Check it1.second 55 float.
// Continue.
dummyStatement(&map, &it1, &it2, &it3, &it4, &it5, &it6);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment