Commit 5a8b61b2 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Great debugger helper bug fixing spree.

- Fix extractTemplate() not to kill blanks in case there is no
  blank after a comma, fix hardcoded types accordingly.
- Make niceType()'s regexps ignore blanks after commas, add
  unsigned short as char_type and cache the mappings, making
  them work for CDB.
- CDB: Parse map output of dumpers correctly, generally don't
  confuse the parser by unknown keywords encountered when
  parsing children, thus enabling simple QMap types.
- Make dumpInnerValueHelper output std::string types.
parent 89d49e3b
......@@ -844,6 +844,26 @@ static void qDumpUnknown(QDumper &d, const char *why = 0)
d.disarm();
}
static inline void dumpStdStringValue(QDumper &d, const std::string &str)
{
d.beginItem("value");
d.putBase64Encoded(str.c_str(), str.size());
d.endItem();
d.putItem("valueencoded", "1");
d.putItem("type", "std::string");
d.putItem("numchild", "0");
}
static inline void dumpStdWStringValue(QDumper &d, const std::wstring &str)
{
d.beginItem("value");
d.putBase64Encoded((const char *)str.c_str(), str.size() * sizeof(wchar_t));
d.endItem();
d.putItem("valueencoded", (sizeof(wchar_t) == 2 ? "2" : "3"));
d.putItem("type", "std::wstring");
d.putItem("numchild", "0");
}
static void qDumpInnerValueHelper(QDumper &d, const char *type, const void *addr,
const char *field = "value")
{
......@@ -928,6 +948,16 @@ static void qDumpInnerValueHelper(QDumper &d, const char *type, const void *addr
d.putItem(field, *(QString*)addr);
}
return;
case 't':
if (isEqual(type, "std::string")
|| isEqual(type, "std::basic_string<char,std::char_traits<char>,std::allocator<char> >")) {
d.putCommaIfNeeded();
dumpStdStringValue(d, *reinterpret_cast<const std::string*>(addr));
} else if (isEqual(type, "std::wstring")
|| isEqual(type, "std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >")) {
dumpStdWStringValue(d, *reinterpret_cast<const std::wstring*>(addr));
}
return;
default:
return;
}
......@@ -944,7 +974,6 @@ static void qDumpInnerValue(QDumper &d, const char *type, const void *addr)
qDumpInnerValueHelper(d, type, addr);
}
static void qDumpInnerValueOrPointer(QDumper &d,
const char *type, const char *strippedtype, const void *addr)
{
......@@ -2753,37 +2782,28 @@ static void qDumpStdString(QDumper &d)
{
const std::string &str = *reinterpret_cast<const std::string *>(d.data);
if (!str.empty()) {
const std::string::size_type size = str.size();
if (int(size) < 0)
return;
if (size) {
qCheckAccess(str.c_str());
qCheckAccess(str.c_str() + str.size() - 1);
qCheckAccess(str.c_str() + size - 1);
}
d.beginItem("value");
d.putBase64Encoded(str.c_str(), str.size());
d.endItem();
d.putItem("valueencoded", "1");
d.putItem("type", "std::string");
d.putItem("numchild", "0");
dumpStdStringValue(d, str);
d.disarm();
}
static void qDumpStdWString(QDumper &d)
{
const std::wstring &str = *reinterpret_cast<const std::wstring *>(d.data);
if (!str.empty()) {
const std::wstring::size_type size = str.size();
if (int(size) < 0)
return;
if (size) {
qCheckAccess(str.c_str());
qCheckAccess(str.c_str() + str.size() - 1);
qCheckAccess(str.c_str() + size - 1);
}
d.beginItem("value");
d.putBase64Encoded((const char *)str.c_str(), str.size() * sizeof(wchar_t));
d.endItem();
d.putItem("valueencoded", (sizeof(wchar_t) == 2 ? "2" : "3"));
d.putItem("type", "std::wstring");
d.putItem("numchild", "0");
dumpStdWStringValue(d, str);
d.disarm();
}
......@@ -3164,6 +3184,8 @@ void *qDumpObjectData440(
.put("std::string=\"").put(sizeof(std::string)).put("\",")
.put("std::wstring=\"").put(sizeof(std::wstring)).put("\",")
.put("std::allocator=\"").put(sizeof(std::allocator<int>)).put("\",")
.put("std::char_traits<char>=\"").put(sizeof(std::char_traits<char>)).put("\",")
.put("std::char_traits<unsigned short>=\"").put(sizeof(std::char_traits<unsigned short>)).put("\",")
#if QT_VERSION >= 0x040500
.put(NS"QSharedPointer=\"").put(sizeof(QSharedPointer<int>)).put("\",")
.put(NS"QSharedDataPointer=\"").put(sizeof(QSharedDataPointer<QSharedData>)).put("\",")
......
......@@ -256,6 +256,18 @@ static int dumpStdStringVector()
return 0;
}
static int dumpStdWStringVector()
{
std::vector<std::wstring> test;
test.push_back(L"item1");
test.push_back(L"item2");
prepareInBuffer("std::vector", "local.wstringvector", "local.wstringvector", "std::wstring");
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(std::wstring), sizeof(std::list<int>::allocator_type), 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
}
static int dumpStdIntSet()
{
std::set<int> test;
......@@ -335,6 +347,8 @@ static bool dumpType(const char *arg)
{ dumpStdIntVector(); return true; }
if (!qstrcmp(arg, "vector<string>"))
{ dumpStdStringVector(); return true; }
if (!qstrcmp(arg, "vector<wstring>"))
{ dumpStdWStringVector(); return true; }
if (!qstrcmp(arg, "set<int>"))
{ dumpStdIntSet(); return true; }
if (!qstrcmp(arg, "set<string>"))
......
......@@ -544,12 +544,23 @@ static inline QString msgDumpFailed(const WatchData &wd, const QString *why)
return QString::fromLatin1("Unable to dump '%1' (%2): %3").arg(wd.name, wd.type, *why);
}
static inline QString msgNotHandled(const QString &type)
{
return QString::fromLatin1("The type '%1' is not handled.").arg(type);
}
CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool dumpChildren, int source,
QList<WatchData> *result, QString *errorMessage)
{
// Check failure cache and supported types
if (m_state == Disabled || m_failedTypes.contains(wd.type))
if (m_state == Disabled) {
*errorMessage = QLatin1String("Dumpers are disabled");
return DumpNotHandled;
}
if (m_failedTypes.contains(wd.type)) {
*errorMessage = msgNotHandled(wd.type);
return DumpNotHandled;
}
// Ensure types are parsed and known.
if (!ensureInitialized(errorMessage)) {
......@@ -562,8 +573,10 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool
const QtDumperHelper::TypeData td = m_helper.typeData(wd.type);
if (loadDebug)
qDebug() << "dumpType" << wd.type << td;
if (td.type == QtDumperHelper::UnknownType)
if (td.type == QtDumperHelper::UnknownType) {
*errorMessage = msgNotHandled(wd.type);
return DumpNotHandled;
}
// Now evaluate
const QString message = QCoreApplication::translate("Debugger::Internal::CdbDumperHelper",
......
......@@ -257,7 +257,7 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal,
foreach(const WatchData &dwd, dumperResult)
wh->insertData(dwd);
} else {
const QString msg = QString::fromLatin1("Unable to further expand dumper watch data: %1 (%2): %3/%4").arg(incompleteLocal.name, incompleteLocal.type).arg(int(dr)).arg(*errorMessage);
const QString msg = QString::fromLatin1("Unable to further expand dumper watch data: '%1' (%2): %3/%4").arg(incompleteLocal.name, incompleteLocal.type).arg(int(dr)).arg(*errorMessage);
qWarning("%s", qPrintable(msg));
WatchData wd = incompleteLocal;
wd.setAllUnneeded();
......
......@@ -144,7 +144,7 @@ void ToolTipWidget::done()
}
void ToolTipWidget::run(const QPoint &point, QAbstractItemModel *model,
const QModelIndex &index, const QString &msg)
const QModelIndex &index, const QString & /* msg */)
{
move(point);
setModel(model);
......
......@@ -390,9 +390,29 @@ static QString chopConst(QString type)
return type;
}
QString niceType(QString type)
{
type.replace('*', '@');
static inline QRegExp stdStringRegExp(const QString &charType)
{
QString rc = QLatin1String("basic_string<");
rc += charType;
rc += QLatin1String(",[ ]?std::char_traits<");
rc += charType;
rc += QLatin1String(">,[ ]?std::allocator<");
rc += charType;
rc += QLatin1String("> >");
const QRegExp re(rc);
Q_ASSERT(re.isValid());
return re;
}
QString niceType(const QString typeIn)
{
static QMap<QString, QString> cache;
const QMap<QString, QString>::const_iterator it = cache.constFind(typeIn);
if (it != cache.constEnd()) {
return it.value();
}
QString type = typeIn;
type.replace(QLatin1Char('*'), QLatin1Char('@'));
for (int i = 0; i < 10; ++i) {
int start = type.indexOf("std::allocator<");
......@@ -414,33 +434,37 @@ QString niceType(QString type)
QString alloc = type.mid(start, pos + 1 - start).trimmed();
QString inner = alloc.mid(15, alloc.size() - 16).trimmed();
if (inner == QLatin1String("char"))
// std::string
type.replace(QLatin1String("basic_string<char, std::char_traits<char>, "
"std::allocator<char> >"), QLatin1String("string"));
else if (inner == QLatin1String("wchar_t"))
// std::wstring
type.replace(QLatin1String("basic_string<wchar_t, std::char_traits<wchar_t>, "
"std::allocator<wchar_t> >"), QLatin1String("wstring"));
if (inner == QLatin1String("char")) { // std::string
static const QRegExp stringRegexp = stdStringRegExp(inner);
type.replace(stringRegexp, QLatin1String("string"));
} else if (inner == QLatin1String("wchar_t")) { // std::wstring
static const QRegExp wchartStringRegexp = stdStringRegExp(inner);
type.replace(wchartStringRegexp, QLatin1String("wstring"));
} else if (inner == QLatin1String("unsigned short")) { // std::wstring/MSVC
static const QRegExp usStringRegexp = stdStringRegExp(inner);
type.replace(usStringRegexp, QLatin1String("wstring"));
}
// std::vector, std::deque, std::list
QRegExp re1(QString("(vector|list|deque)<%1, %2\\s*>").arg(inner, alloc));
static const QRegExp re1(QString::fromLatin1("(vector|list|deque)<%1,[ ]?%2\\s*>").arg(inner, alloc));
Q_ASSERT(re1.isValid());
if (re1.indexIn(type) != -1)
type.replace(re1.cap(0), QString("%1<%2>").arg(re1.cap(1), inner));
type.replace(re1.cap(0), QString::fromLatin1("%1<%2>").arg(re1.cap(1), inner));
// std::stack
QRegExp re6(QString("stack<%1, std::deque<%2> >").arg(inner, inner));
re6.setMinimal(true);
static QRegExp re6(QString::fromLatin1("stack<%1,[ ]?std::deque<%2> >").arg(inner, inner));
if (!re6.isMinimal())
re6.setMinimal(true);
Q_ASSERT(re6.isValid());
if (re6.indexIn(type) != -1)
type.replace(re6.cap(0), QString("stack<%1>").arg(inner));
type.replace(re6.cap(0), QString::fromLatin1("stack<%1>").arg(inner));
// std::set
QRegExp re4(QString("set<%1, std::less<%2>, %3\\s*>").arg(inner, inner, alloc));
re4.setMinimal(true);
static QRegExp re4(QString::fromLatin1("set<%1,[ ]?std::less<%2>,[ ]?%3\\s*>").arg(inner, inner, alloc));
if (!re4.isMinimal())
re4.setMinimal(true);
Q_ASSERT(re4.isValid());
if (re4.indexIn(type) != -1)
type.replace(re4.cap(0), QString("set<%1>").arg(inner));
type.replace(re4.cap(0), QString::fromLatin1("set<%1>").arg(inner));
// std::map
if (inner.startsWith("std::pair<")) {
......@@ -460,22 +484,26 @@ QString niceType(QString type)
QString key = chopConst(ckey);
QString value = inner.mid(pos + 2, inner.size() - 3 - pos);
QRegExp re5(QString("map<%1, %2, std::less<%3>, %4\\s*>")
static QRegExp re5(QString("map<%1,[ ]?%2,[ ]?std::less<%3>,[ ]?%4\\s*>")
.arg(key, value, key, alloc));
re5.setMinimal(true);
if (!re5.isMinimal())
re5.setMinimal(true);
Q_ASSERT(re5.isValid());
if (re5.indexIn(type) != -1)
type.replace(re5.cap(0), QString("map<%1, %2>").arg(key, value));
else {
QRegExp re7(QString("map<const %1, %2, std::less<const %3>, %4\\s*>")
static QRegExp re7(QString("map<const %1,[ ]?%2,[ ]?std::less<const %3>,[ ]?%4\\s*>")
.arg(key, value, key, alloc));
re7.setMinimal(true);
if (!re7.isMinimal())
re7.setMinimal(true);
if (re7.indexIn(type) != -1)
type.replace(re7.cap(0), QString("map<const %1, %2>").arg(key, value));
}
}
}
type.replace('@', '*');
type.replace(QLatin1Char('@'), QLatin1Char('*'));
type.replace(QLatin1String(" >"), QString(QLatin1Char('>')));
cache.insert(typeIn, type); // For simplicity, also cache unmodified types
return type;
}
......
......@@ -266,30 +266,43 @@ bool extractTemplate(const QString &type, QString *tmplate, QString *inner)
// Input "Template<Inner1,Inner2,...>::Foo" will return "Template::Foo" in
// 'tmplate' and "Inner1@Inner2@..." etc in 'inner'. Result indicates
// whether parsing was successful
// Gdb inserts a blank after each comma which we would like to avoid
tmplate->clear();
inner->clear();
if (!type.contains(QLatin1Char('<')))
return false;
int level = 0;
bool skipSpace = false;
const int size = type.size();
for (int i = 0; i != type.size(); ++i) {
for (int i = 0; i != size; ++i) {
const QChar c = type.at(i);
if (c == QLatin1Char(' ') && skipSpace) {
skipSpace = false;
} else if (c == QLatin1Char('<')) {
const char asciiChar = c.toAscii();
switch (asciiChar) {
case '<':
*(level == 0 ? tmplate : inner) += c;
++level;
} else if (c == QLatin1Char('>')) {
break;
case '>':
--level;
*(level == 0 ? tmplate : inner) += c;
} else if (c == QLatin1Char(',')) {
break;
case ',':
*inner += (level == 1) ? QLatin1Char('@') : QLatin1Char(',');
skipSpace = true;
} else {
*(level == 0 ? tmplate : inner) += c;
break;
default:
if (!skipSpace || asciiChar != ' ') {
*(level == 0 ? tmplate : inner) += c;
skipSpace = false;
}
break;
}
}
*tmplate = tmplate->trimmed();
*tmplate = tmplate->remove(QLatin1String("<>"));
*inner = inner->trimmed();
//qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner << " FROM " << type;
// qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner << " FROM " << type;
return !inner->isEmpty();
}
......@@ -408,7 +421,7 @@ bool isCppEditor(Core::IEditor *editor)
// Find the function the cursor is in to use a scope.
// Return the Cpp expression, and, if desired, the function
......@@ -442,7 +455,7 @@ QString cppExpressionAt(TextEditor::ITextEditor *editor, int pos,
const QTextCursor tc = plaintext->textCursor();
*column = tc.columnNumber();
*line = tc.blockNumber();
}
}
if (function && !expr.isEmpty())
if (const Core::IFile *file = editor->file())
......@@ -455,6 +468,7 @@ QString cppExpressionAt(TextEditor::ITextEditor *editor, int pos,
// --------------- QtDumperResult
QtDumperResult::Child::Child() :
keyEncoded(0),
valueEncoded(0),
childCount(0),
valuedisabled(false)
......@@ -475,6 +489,7 @@ void QtDumperResult::clear()
value.clear();
address.clear();
type.clear();
extra.clear();
displayedType.clear();
valueEncoded = 0;
valuedisabled = false;
......@@ -509,8 +524,17 @@ QList<WatchData> QtDumperResult::toWatchData(int source) const
wchild.source = source;
wchild.iname = iname;
wchild.iname += dot;
wchild.iname += dchild.name;
wchild.name = dchild.name;
wchild.iname += dchild.name;
// Use key entry as name (which is used for map nodes)
if (dchild.key.isEmpty()) {
wchild.name = dchild.name;
} else {
wchild.name = decodeData(dchild.key, dchild.keyEncoded);
if (wchild.name.size() > 13) {
wchild.name.truncate(12);
wchild.name += QLatin1String("...");
}
}
wchild.exp = dchild.exp;
wchild.valuedisabled = dchild.valuedisabled;
wchild.setType(dchild.type.isEmpty() ? childType : dchild.type);
......@@ -533,18 +557,21 @@ QDebug operator<<(QDebug in, const QtDumperResult &d)
<< " address=" << d.address
<< " value=" << d.value
<< " disabled=" << d.valuedisabled
<< " encoded=" << d.valueEncoded << " internal=" << d.internal;
<< " encoded=" << d.valueEncoded << " internal=" << d.internal
<< " extra='" << d.extra << "'\n";
const int realChildCount = d.children.size();
if (d.childCount || realChildCount) {
nospace << " childCount=" << d.childCount << '/' << realChildCount
nospace << "childCount=" << d.childCount << '/' << realChildCount
<< " childType=" << d.childType << '\n';
for (int i = 0; i < realChildCount; i++) {
const QtDumperResult::Child &c = d.children.at(i);
nospace << " #" << i << " addr=" << c.address
<< " disabled=" << c.valuedisabled
<< " type=" << c.type << " exp=" << c.exp
<< " name=" << c.name << " encoded=" << c.valueEncoded
<< " value=" << c.value
<< " name=" << c.name;
if (!c.key.isEmpty())
nospace << " keyencoded=" << c.keyEncoded << " key=" << c.key;
nospace << " valueencoded=" << c.valueEncoded << " value=" << c.value
<< "childcount=" << c.childCount << '\n';
}
}
......@@ -770,6 +797,7 @@ class DumperParser
public:
explicit DumperParser(const char *s) : m_s(s) {}
bool run();
virtual ~DumperParser() {}
protected:
// handle 'key="value"'
......@@ -833,9 +861,9 @@ bool DumperParser::parseHash(int level, const char *&pos)
return false;
pos = equalsPtr + 1;
if (!*pos)
return false;
return false;
if (!parseValue(level + 1, pos))
return false;
return false;
if (*pos == ',')
pos++;
}
......@@ -871,7 +899,7 @@ bool DumperParser::parseValue(int level, const char *&pos)
if (*pos == ',')
pos++;
}
}
}
return false;
// A hash '{a="b",b="c"}'
case '{': {
......@@ -879,7 +907,7 @@ bool DumperParser::parseValue(int level, const char *&pos)
return false;
pos++;
if (!parseHash(level + 1, pos))
return false;
return false;
return handleHashEnd();
}
return false;
......@@ -952,7 +980,7 @@ public:
protected:
virtual bool handleKeyword(const char *k, int size);
virtual bool handleListStart();
virtual bool handleListStart();
virtual bool handleListEnd();
virtual bool handleHashEnd();
virtual bool handleValue(const char *k, int size);
......@@ -972,7 +1000,7 @@ QueryDumperParser::QueryDumperParser(const char *s) :
{
}
bool QueryDumperParser::handleKeyword(const char *k, int size)
bool QueryDumperParser::handleKeyword(const char *k, int size)
{
switch (m_mode) {
case ExpectingSizes:
......@@ -1064,7 +1092,6 @@ bool QtDumperHelper::parseQuery(const char *data, Debugger debugger)
foreach (const QueryDumperParser::SizeEntry &se, parser.data().sizes)
addSize(se.first, se.second);
m_expressionCache = parser.data().expressionCache;
qDebug() << m_expressionCache;
return true;
}
......@@ -1081,13 +1108,15 @@ void QtDumperHelper::addSize(const QString &name, int size)
return;
}
do {
// CDB helpers
if (name == QLatin1String("std::string")) {
m_sizeCache.insert(QLatin1String("std::basic_string<char,std::char_traits<char>,std::allocator<char>>"), size);
m_sizeCache.insert(QLatin1String("std::basic_string<char,std::char_traits<char>,std::allocator<char> >"), size);
m_sizeCache.insert(QLatin1String("basic_string<char,char_traits<char>,allocator<char> >"), size);
break;
}
if (name == QLatin1String("std::wstring")) {
// FIXME: check space between > > below?
m_sizeCache.insert(QLatin1String("std::basic_string<unsigned short,std::char_traits<unsignedshort>,std::allocator<unsignedshort> >"), size);
m_sizeCache.insert(QLatin1String("basic_string<unsigned short,char_traits<unsignedshort>,allocator<unsignedshort> >"), size);
m_sizeCache.insert(QLatin1String("std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >"), size);
break;
}
} while (false);
......@@ -1297,10 +1326,16 @@ void QtDumperHelper::evaluationParameters(const WatchData &data,
// But we need the offset of the second item in the value pair.
// We read the type of the pair from the allocator argument because
// that gets the constness "right" (in the sense that gdb can
// read it back;
// read it back: "std::allocator<std::pair<Key,Value> >"
// -> "std::pair<Key,Value>". Different debuggers have varying
// amounts of terminating blanks...
QString pairType = inners.at(3);
// remove 'std::allocator<...>':
pairType = pairType.mid(15, pairType.size() - 15 - 2);
int bracketPos = pairType.indexOf(QLatin1Char('<'));
if (bracketPos != -1)
pairType.remove(0, bracketPos + 1);
bracketPos = pairType.indexOf(QLatin1Char('>'));
if (bracketPos != -1)
pairType.truncate(bracketPos + 1);
extraArgs[2] = QLatin1String("(size_t)&(('");
extraArgs[2] += pairType;
extraArgs[2] += QLatin1String("'*)0)->second");
......@@ -1377,12 +1412,15 @@ private:
ExpectingType, ExpectingDisplayedType, ExpectingInternal,
ExpectingValueDisabled, ExpectingValueEncoded,
ExpectingCommonChildType, ExpectingChildCount,
ExpectingExtra,
IgnoreNext,
ChildModeStart,
ExpectingChildren,ExpectingChildName, ExpectingChildAddress,
ExpectingChildExpression, ExpectingChildType,
ExpectingChildKey, ExpectingChildKeyEncoded,
ExpectingChildValue, ExpectingChildValueEncoded,
ExpectingChildValueDisabled, ExpectingChildChildCount
ExpectingChildValueDisabled, ExpectingChildChildCount,
IgnoreNextChildMode
};
static inline Mode nextMode(Mode in, const char *keyword, int size);
......@@ -1405,6 +1443,8 @@ ValueDumperParser::Mode ValueDumperParser::nextMode(Mode in, const char *keyword
case 3:
if (!qstrncmp(keyword, "exp", size))
return ExpectingChildExpression;
if (!qstrncmp(keyword, "key", size))
return ExpectingChildKey;
break;
case 4:
if (!qstrncmp(keyword, "addr", size))
......@@ -1419,6 +1459,8 @@ ValueDumperParser::Mode ValueDumperParser::nextMode(Mode in, const char *keyword
return ExpectingIName;
if (!qstrncmp(keyword, "value", size))
return in > ChildModeStart ? ExpectingChildValue : ExpectingValue;
if (!qstrncmp(keyword, "extra", size))
return ExpectingExtra;
break;
case 8:
if (!qstrncmp(keyword, "children", size))
......@@ -1431,7 +1473,11 @@ ValueDumperParser::Mode ValueDumperParser::nextMode(Mode in, const char *keyword
case 9:
if (!qstrncmp(keyword, "childtype", size))
return ExpectingCommonChildType;
break;
break;
case 10:
if (!qstrncmp(keyword, "keyencoded", size))
return ExpectingChildKeyEncoded;
break;