forked from qt-creator/qt-creator
Initial work on dumpers for CDB.
Add dumper calls, parser etc. Catch crashes in the dumper functions.
This commit is contained in:
@@ -60,6 +60,10 @@ int qtGhVersion = QT_VERSION;
|
|||||||
# include <QtGui/QImage>
|
# include <QtGui/QImage>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
# include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -231,11 +235,17 @@ static QByteArray stripPointerType(QByteArray type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is used to abort evaluation of custom data dumpers in a "coordinated"
|
// This is used to abort evaluation of custom data dumpers in a "coordinated"
|
||||||
// way. Abortion will happen anyway when we try to access a non-initialized
|
// way. Abortion will happen at the latest when we try to access a non-initialized
|
||||||
// non-trivial object, so there is no way to prevent this from occuring at all
|
// non-trivial object, so there is no way to prevent this from occuring at all
|
||||||
// conceptionally. Gdb will catch SIGSEGV and return to the calling frame.
|
// conceptionally. Ideally, if there is API to check memory access, it should
|
||||||
|
// be used to terminate nicely, especially with CDB.
|
||||||
|
// 1) Gdb will catch SIGSEGV and return to the calling frame.
|
||||||
// This is just fine provided we only _read_ memory in the custom handlers
|
// This is just fine provided we only _read_ memory in the custom handlers
|
||||||
// below.
|
// below.
|
||||||
|
// 2) For MSVC/CDB, exceptions must be handled in the dumper, which is
|
||||||
|
// achieved using __try/__except. The exception will be reported in the
|
||||||
|
// debugger, which will then execute a 'gN' command, passing handling back
|
||||||
|
// to the __except clause.
|
||||||
|
|
||||||
volatile int qProvokeSegFaultHelper;
|
volatile int qProvokeSegFaultHelper;
|
||||||
|
|
||||||
@@ -269,11 +279,16 @@ static bool startsWith(const char *s, const char *t)
|
|||||||
return qstrncmp(s, t, qstrlen(t)) == 0;
|
return qstrncmp(s, t, qstrlen(t)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// provoke segfault when address is not readable
|
// Check memory for read access and provoke segfault if nothing else helps.
|
||||||
#define qCheckAccess(d) do { qProvokeSegFaultHelper = *(char*)d; } while (0)
|
// On Windows, try to be less crash-prone by checking memory using WinAPI
|
||||||
#define qCheckPointer(d) do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0)
|
|
||||||
// provoke segfault unconditionally
|
#ifdef Q_OS_WIN
|
||||||
#define qCheck(b) do { if (!(b)) qProvokeSegFaultHelper = *(char*)0; } while (0)
|
# define qCheckAccess(d) if (IsBadReadPtr(d, 1)) return false; do { qProvokeSegFaultHelper = *(char*)d; } while (0)
|
||||||
|
# define qCheckPointer(d) if (d && IsBadReadPtr(d, 1)) return false; do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0)
|
||||||
|
#else
|
||||||
|
# define qCheckAccess(d) do { qProvokeSegFaultHelper = *(char*)d; } while (0)
|
||||||
|
# define qCheckPointer(d) do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
const char *stripNamespace(const char *type)
|
const char *stripNamespace(const char *type)
|
||||||
{
|
{
|
||||||
@@ -692,11 +707,14 @@ void QDumper::putEllipsis()
|
|||||||
#define TT(type, value) \
|
#define TT(type, value) \
|
||||||
"<tr><td>" << type << "</td><td> : </td><td>" << value << "</td></tr>"
|
"<tr><td>" << type << "</td><td> : </td><td>" << value << "</td></tr>"
|
||||||
|
|
||||||
static void qDumpUnknown(QDumper &d)
|
#define DUMPUNKNOWN_MESSAGE "<internal error>"
|
||||||
|
static void qDumpUnknown(QDumper &d, const char *why = 0)
|
||||||
{
|
{
|
||||||
P(d, "iname", d.iname);
|
P(d, "iname", d.iname);
|
||||||
P(d, "addr", d.data);
|
P(d, "addr", d.data);
|
||||||
P(d, "value", "<internal error>");
|
if (!why)
|
||||||
|
why = DUMPUNKNOWN_MESSAGE;
|
||||||
|
P(d, "value", why);
|
||||||
P(d, "type", d.outertype);
|
P(d, "type", d.outertype);
|
||||||
P(d, "numchild", "0");
|
P(d, "numchild", "0");
|
||||||
d.disarm();
|
d.disarm();
|
||||||
@@ -815,7 +833,7 @@ static void qDumpInnerValueOrPointer(QDumper &d,
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static void qDumpQByteArray(QDumper &d)
|
static bool qDumpQByteArray(QDumper &d)
|
||||||
{
|
{
|
||||||
const QByteArray &ba = *reinterpret_cast<const QByteArray *>(d.data);
|
const QByteArray &ba = *reinterpret_cast<const QByteArray *>(d.data);
|
||||||
|
|
||||||
@@ -848,6 +866,7 @@ static void qDumpQByteArray(QDumper &d)
|
|||||||
d << "]";
|
d << "]";
|
||||||
}
|
}
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQDateTime(QDumper &d)
|
static void qDumpQDateTime(QDumper &d)
|
||||||
@@ -1063,7 +1082,7 @@ int hashOffset(bool optimizedIntKey, bool forKey, unsigned keySize, unsigned val
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void qDumpQHash(QDumper &d)
|
static bool qDumpQHash(QDumper &d)
|
||||||
{
|
{
|
||||||
QHashData *h = *reinterpret_cast<QHashData *const*>(d.data);
|
QHashData *h = *reinterpret_cast<QHashData *const*>(d.data);
|
||||||
const char *keyType = d.templateParameters[0];
|
const char *keyType = d.templateParameters[0];
|
||||||
@@ -1078,7 +1097,7 @@ static void qDumpQHash(QDumper &d)
|
|||||||
int n = h->size;
|
int n = h->size;
|
||||||
|
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
qCheck(false);
|
return false;
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
qCheckPointer(h->fakeNext);
|
qCheckPointer(h->fakeNext);
|
||||||
qCheckPointer(*h->buckets);
|
qCheckPointer(*h->buckets);
|
||||||
@@ -1127,6 +1146,7 @@ static void qDumpQHash(QDumper &d)
|
|||||||
d << "]";
|
d << "]";
|
||||||
}
|
}
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQHashNode(QDumper &d)
|
static void qDumpQHashNode(QDumper &d)
|
||||||
@@ -1177,23 +1197,25 @@ static void qDumpQImage(QDumper &d)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQList(QDumper &d)
|
static bool qDumpQList(QDumper &d)
|
||||||
{
|
{
|
||||||
// This uses the knowledge that QList<T> has only a single member
|
// This uses the knowledge that QList<T> has only a single member
|
||||||
// of type union { QListData p; QListData::Data *d; };
|
// of type union { QListData p; QListData::Data *d; };
|
||||||
|
|
||||||
const QListData &ldata = *reinterpret_cast<const QListData*>(d.data);
|
const QListData &ldata = *reinterpret_cast<const QListData*>(d.data);
|
||||||
const QListData::Data *pdata =
|
const QListData::Data *pdata =
|
||||||
*reinterpret_cast<const QListData::Data* const*>(d.data);
|
*reinterpret_cast<const QListData::Data* const*>(d.data);
|
||||||
|
qCheckAccess(pdata);
|
||||||
int nn = ldata.size();
|
int nn = ldata.size();
|
||||||
if (nn < 0)
|
if (nn < 0)
|
||||||
qCheck(false);
|
return false;
|
||||||
if (nn > 0) {
|
if (nn > 0) {
|
||||||
qCheckAccess(ldata.d->array);
|
qCheckAccess(ldata.d->array);
|
||||||
//qCheckAccess(ldata.d->array[0]);
|
//qCheckAccess(ldata.d->array[0]);
|
||||||
//qCheckAccess(ldata.d->array[nn - 1]);
|
//qCheckAccess(ldata.d->array[nn - 1]);
|
||||||
#if QT_VERSION >= 0x040400
|
#if QT_VERSION >= 0x040400
|
||||||
if (ldata.d->ref._q_value <= 0)
|
if (ldata.d->ref._q_value <= 0)
|
||||||
qCheck(false);
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1252,9 +1274,10 @@ static void qDumpQList(QDumper &d)
|
|||||||
d << "]";
|
d << "]";
|
||||||
}
|
}
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQLinkedList(QDumper &d)
|
static bool qDumpQLinkedList(QDumper &d)
|
||||||
{
|
{
|
||||||
// This uses the knowledge that QLinkedList<T> has only a single member
|
// This uses the knowledge that QLinkedList<T> has only a single member
|
||||||
// of type union { QLinkedListData *d; QLinkedListNode<T> *e; };
|
// of type union { QLinkedListData *d; QLinkedListNode<T> *e; };
|
||||||
@@ -1262,7 +1285,7 @@ static void qDumpQLinkedList(QDumper &d)
|
|||||||
reinterpret_cast<const QLinkedListData*>(deref(d.data));
|
reinterpret_cast<const QLinkedListData*>(deref(d.data));
|
||||||
int nn = ldata->size;
|
int nn = ldata->size;
|
||||||
if (nn < 0)
|
if (nn < 0)
|
||||||
qCheck(false);
|
return false;
|
||||||
|
|
||||||
int n = nn;
|
int n = nn;
|
||||||
P(d, "value", "<" << n << " items>");
|
P(d, "value", "<" << n << " items>");
|
||||||
@@ -1294,6 +1317,7 @@ static void qDumpQLinkedList(QDumper &d)
|
|||||||
d << "]";
|
d << "]";
|
||||||
}
|
}
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQLocale(QDumper &d)
|
static void qDumpQLocale(QDumper &d)
|
||||||
@@ -1341,7 +1365,7 @@ static void qDumpQLocale(QDumper &d)
|
|||||||
d.disarm();
|
d.disarm();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQMapNode(QDumper &d)
|
static bool qDumpQMapNode(QDumper &d)
|
||||||
{
|
{
|
||||||
const QMapData *h = reinterpret_cast<const QMapData *>(d.data);
|
const QMapData *h = reinterpret_cast<const QMapData *>(d.data);
|
||||||
const char *keyType = d.templateParameters[0];
|
const char *keyType = d.templateParameters[0];
|
||||||
@@ -1375,9 +1399,10 @@ static void qDumpQMapNode(QDumper &d)
|
|||||||
}
|
}
|
||||||
|
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQMap(QDumper &d)
|
static bool qDumpQMap(QDumper &d)
|
||||||
{
|
{
|
||||||
QMapData *h = *reinterpret_cast<QMapData *const*>(d.data);
|
QMapData *h = *reinterpret_cast<QMapData *const*>(d.data);
|
||||||
const char *keyType = d.templateParameters[0];
|
const char *keyType = d.templateParameters[0];
|
||||||
@@ -1386,7 +1411,7 @@ static void qDumpQMap(QDumper &d)
|
|||||||
int n = h->size;
|
int n = h->size;
|
||||||
|
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
qCheck(false);
|
return false;
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
qCheckAccess(h->backward);
|
qCheckAccess(h->backward);
|
||||||
qCheckAccess(h->forward[0]);
|
qCheckAccess(h->forward[0]);
|
||||||
@@ -1455,11 +1480,12 @@ static void qDumpQMap(QDumper &d)
|
|||||||
}
|
}
|
||||||
|
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQMultiMap(QDumper &d)
|
static bool qDumpQMultiMap(QDumper &d)
|
||||||
{
|
{
|
||||||
qDumpQMap(d);
|
return qDumpQMap(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQModelIndex(QDumper &d)
|
static void qDumpQModelIndex(QDumper &d)
|
||||||
@@ -1890,7 +1916,7 @@ static void qDumpQPixmap(QDumper &d)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQSet(QDumper &d)
|
static bool qDumpQSet(QDumper &d)
|
||||||
{
|
{
|
||||||
// This uses the knowledge that QHash<T> has only a single member
|
// This uses the knowledge that QHash<T> has only a single member
|
||||||
// of union { QHashData *d; QHashNode<Key, T> *e; };
|
// of union { QHashData *d; QHashNode<Key, T> *e; };
|
||||||
@@ -1899,7 +1925,7 @@ static void qDumpQSet(QDumper &d)
|
|||||||
|
|
||||||
int n = hd->size;
|
int n = hd->size;
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
qCheck(false);
|
return false;
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
qCheckAccess(node);
|
qCheckAccess(node);
|
||||||
qCheckPointer(node->next);
|
qCheckPointer(node->next);
|
||||||
@@ -1933,6 +1959,7 @@ static void qDumpQSet(QDumper &d)
|
|||||||
d << "]";
|
d << "]";
|
||||||
}
|
}
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQSharedPointer(QDumper &d)
|
static void qDumpQSharedPointer(QDumper &d)
|
||||||
@@ -1959,7 +1986,7 @@ static void qDumpQSharedPointer(QDumper &d)
|
|||||||
d.disarm();
|
d.disarm();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQString(QDumper &d)
|
static bool qDumpQString(QDumper &d)
|
||||||
{
|
{
|
||||||
const QString &str = *reinterpret_cast<const QString *>(d.data);
|
const QString &str = *reinterpret_cast<const QString *>(d.data);
|
||||||
|
|
||||||
@@ -1975,14 +2002,15 @@ static void qDumpQString(QDumper &d)
|
|||||||
P(d, "numchild", "0");
|
P(d, "numchild", "0");
|
||||||
|
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQStringList(QDumper &d)
|
static bool qDumpQStringList(QDumper &d)
|
||||||
{
|
{
|
||||||
const QStringList &list = *reinterpret_cast<const QStringList *>(d.data);
|
const QStringList &list = *reinterpret_cast<const QStringList *>(d.data);
|
||||||
int n = list.size();
|
int n = list.size();
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
qCheck(false);
|
return false;
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
qCheckAccess(&list.front());
|
qCheckAccess(&list.front());
|
||||||
qCheckAccess(&list.back());
|
qCheckAccess(&list.back());
|
||||||
@@ -2009,6 +2037,7 @@ static void qDumpQStringList(QDumper &d)
|
|||||||
d << "]";
|
d << "]";
|
||||||
}
|
}
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQTextCodec(QDumper &d)
|
static void qDumpQTextCodec(QDumper &d)
|
||||||
@@ -2109,7 +2138,7 @@ static void qDumpQVariant(QDumper &d)
|
|||||||
d.disarm();
|
d.disarm();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpQVector(QDumper &d)
|
static bool qDumpQVector(QDumper &d)
|
||||||
{
|
{
|
||||||
QVectorData *v = *reinterpret_cast<QVectorData *const*>(d.data);
|
QVectorData *v = *reinterpret_cast<QVectorData *const*>(d.data);
|
||||||
|
|
||||||
@@ -2117,7 +2146,7 @@ static void qDumpQVector(QDumper &d)
|
|||||||
// from asking for unavailable child details
|
// from asking for unavailable child details
|
||||||
int nn = v->size;
|
int nn = v->size;
|
||||||
if (nn < 0)
|
if (nn < 0)
|
||||||
qCheck(false);
|
return false;
|
||||||
if (nn > 0) {
|
if (nn > 0) {
|
||||||
//qCheckAccess(&vec.front());
|
//qCheckAccess(&vec.front());
|
||||||
//qCheckAccess(&vec.back());
|
//qCheckAccess(&vec.back());
|
||||||
@@ -2149,9 +2178,10 @@ static void qDumpQVector(QDumper &d)
|
|||||||
d << "]";
|
d << "]";
|
||||||
}
|
}
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpStdList(QDumper &d)
|
static bool qDumpStdList(QDumper &d)
|
||||||
{
|
{
|
||||||
const std::list<int> &list = *reinterpret_cast<const std::list<int> *>(d.data);
|
const std::list<int> &list = *reinterpret_cast<const std::list<int> *>(d.data);
|
||||||
const void *p = d.data;
|
const void *p = d.data;
|
||||||
@@ -2196,9 +2226,10 @@ static void qDumpStdList(QDumper &d)
|
|||||||
d << "]";
|
d << "]";
|
||||||
}
|
}
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpStdMap(QDumper &d)
|
static bool qDumpStdMap(QDumper &d)
|
||||||
{
|
{
|
||||||
typedef std::map<int, int> DummyType;
|
typedef std::map<int, int> DummyType;
|
||||||
const DummyType &map = *reinterpret_cast<const DummyType*>(d.data);
|
const DummyType &map = *reinterpret_cast<const DummyType*>(d.data);
|
||||||
@@ -2209,7 +2240,8 @@ static void qDumpStdMap(QDumper &d)
|
|||||||
p = deref(p);
|
p = deref(p);
|
||||||
|
|
||||||
int nn = map.size();
|
int nn = map.size();
|
||||||
qCheck(nn >= 0);
|
if (nn < 0)
|
||||||
|
return false;
|
||||||
DummyType::const_iterator it = map.begin();
|
DummyType::const_iterator it = map.begin();
|
||||||
for (int i = 0; i < nn && i < 10 && it != map.end(); ++i, ++it)
|
for (int i = 0; i < nn && i < 10 && it != map.end(); ++i, ++it)
|
||||||
qCheckAccess(it.operator->());
|
qCheckAccess(it.operator->());
|
||||||
@@ -2263,9 +2295,10 @@ static void qDumpStdMap(QDumper &d)
|
|||||||
d << "]";
|
d << "]";
|
||||||
}
|
}
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpStdSet(QDumper &d)
|
static bool qDumpStdSet(QDumper &d)
|
||||||
{
|
{
|
||||||
typedef std::set<int> DummyType;
|
typedef std::set<int> DummyType;
|
||||||
const DummyType &set = *reinterpret_cast<const DummyType*>(d.data);
|
const DummyType &set = *reinterpret_cast<const DummyType*>(d.data);
|
||||||
@@ -2274,7 +2307,8 @@ static void qDumpStdSet(QDumper &d)
|
|||||||
p = deref(p);
|
p = deref(p);
|
||||||
|
|
||||||
int nn = set.size();
|
int nn = set.size();
|
||||||
qCheck(nn >= 0);
|
if (nn < 0)
|
||||||
|
return false;
|
||||||
DummyType::const_iterator it = set.begin();
|
DummyType::const_iterator it = set.begin();
|
||||||
for (int i = 0; i < nn && i < 10 && it != set.end(); ++i, ++it)
|
for (int i = 0; i < nn && i < 10 && it != set.end(); ++i, ++it)
|
||||||
qCheckAccess(it.operator->());
|
qCheckAccess(it.operator->());
|
||||||
@@ -2306,9 +2340,10 @@ static void qDumpStdSet(QDumper &d)
|
|||||||
d << "]";
|
d << "]";
|
||||||
}
|
}
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpStdString(QDumper &d)
|
static bool qDumpStdString(QDumper &d)
|
||||||
{
|
{
|
||||||
const std::string &str = *reinterpret_cast<const std::string *>(d.data);
|
const std::string &str = *reinterpret_cast<const std::string *>(d.data);
|
||||||
|
|
||||||
@@ -2325,9 +2360,10 @@ static void qDumpStdString(QDumper &d)
|
|||||||
P(d, "numchild", "0");
|
P(d, "numchild", "0");
|
||||||
|
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpStdWString(QDumper &d)
|
static bool qDumpStdWString(QDumper &d)
|
||||||
{
|
{
|
||||||
const std::wstring &str = *reinterpret_cast<const std::wstring *>(d.data);
|
const std::wstring &str = *reinterpret_cast<const std::wstring *>(d.data);
|
||||||
|
|
||||||
@@ -2344,9 +2380,10 @@ static void qDumpStdWString(QDumper &d)
|
|||||||
P(d, "numchild", "0");
|
P(d, "numchild", "0");
|
||||||
|
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpStdVector(QDumper &d)
|
static bool qDumpStdVector(QDumper &d)
|
||||||
{
|
{
|
||||||
// Correct type would be something like:
|
// Correct type would be something like:
|
||||||
// std::_Vector_base<int,std::allocator<int, std::allocator<int> >>::_Vector_impl
|
// std::_Vector_base<int,std::allocator<int, std::allocator<int> >>::_Vector_impl
|
||||||
@@ -2361,7 +2398,7 @@ static void qDumpStdVector(QDumper &d)
|
|||||||
// from asking for unavailable child details
|
// from asking for unavailable child details
|
||||||
int nn = (v->finish - v->start) / d.extraInt[0];
|
int nn = (v->finish - v->start) / d.extraInt[0];
|
||||||
if (nn < 0)
|
if (nn < 0)
|
||||||
qCheck(false);
|
return false;
|
||||||
if (nn > 0) {
|
if (nn > 0) {
|
||||||
qCheckAccess(v->start);
|
qCheckAccess(v->start);
|
||||||
qCheckAccess(v->finish);
|
qCheckAccess(v->finish);
|
||||||
@@ -2392,9 +2429,10 @@ static void qDumpStdVector(QDumper &d)
|
|||||||
d << "]";
|
d << "]";
|
||||||
}
|
}
|
||||||
d.disarm();
|
d.disarm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qDumpStdVectorBool(QDumper &d)
|
static bool qDumpStdVectorBool(QDumper &d)
|
||||||
{
|
{
|
||||||
// FIXME
|
// FIXME
|
||||||
return qDumpStdVector(d);
|
return qDumpStdVector(d);
|
||||||
@@ -2402,10 +2440,14 @@ static void qDumpStdVectorBool(QDumper &d)
|
|||||||
|
|
||||||
static void handleProtocolVersion2and3(QDumper & d)
|
static void handleProtocolVersion2and3(QDumper & d)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!d.outertype[0]) {
|
if (!d.outertype[0]) {
|
||||||
qDumpUnknown(d);
|
qDumpUnknown(d);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#ifdef Q_CC_MSVC // Catch exceptions with MSVC/CDB
|
||||||
|
__try {
|
||||||
|
#endif
|
||||||
|
|
||||||
d.setupTemplateParameters();
|
d.setupTemplateParameters();
|
||||||
P(d, "iname", d.iname);
|
P(d, "iname", d.iname);
|
||||||
@@ -2551,6 +2593,12 @@ static void handleProtocolVersion2and3(QDumper & d)
|
|||||||
|
|
||||||
if (!d.success)
|
if (!d.success)
|
||||||
qDumpUnknown(d);
|
qDumpUnknown(d);
|
||||||
|
#ifdef Q_CC_MSVC // Catch exceptions with MSVC/CDB
|
||||||
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
||||||
|
qDumpUnknown(d, DUMPUNKNOWN_MESSAGE" <exception>");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
@@ -34,6 +34,7 @@ HEADERS += \
|
|||||||
$$PWD/cdbdebugoutput.h \
|
$$PWD/cdbdebugoutput.h \
|
||||||
$$PWD/cdbsymbolgroupcontext.h \
|
$$PWD/cdbsymbolgroupcontext.h \
|
||||||
$$PWD/cdbstacktracecontext.h \
|
$$PWD/cdbstacktracecontext.h \
|
||||||
|
$$PWD/cdbstackframecontext.h \
|
||||||
$$PWD/cdbbreakpoint.h \
|
$$PWD/cdbbreakpoint.h \
|
||||||
$$PWD/cdbmodules.h \
|
$$PWD/cdbmodules.h \
|
||||||
$$PWD/cdbassembler.h \
|
$$PWD/cdbassembler.h \
|
||||||
@@ -46,6 +47,7 @@ SOURCES += \
|
|||||||
$$PWD/cdbdebugeventcallback.cpp \
|
$$PWD/cdbdebugeventcallback.cpp \
|
||||||
$$PWD/cdbdebugoutput.cpp \
|
$$PWD/cdbdebugoutput.cpp \
|
||||||
$$PWD/cdbsymbolgroupcontext.cpp \
|
$$PWD/cdbsymbolgroupcontext.cpp \
|
||||||
|
$$PWD/cdbstackframecontext.cpp \
|
||||||
$$PWD/cdbstacktracecontext.cpp \
|
$$PWD/cdbstacktracecontext.cpp \
|
||||||
$$PWD/cdbbreakpoint.cpp \
|
$$PWD/cdbbreakpoint.cpp \
|
||||||
$$PWD/cdbmodules.cpp \
|
$$PWD/cdbmodules.cpp \
|
||||||
|
@@ -29,8 +29,9 @@
|
|||||||
|
|
||||||
#include "cdbdebugengine.h"
|
#include "cdbdebugengine.h"
|
||||||
#include "cdbdebugengine_p.h"
|
#include "cdbdebugengine_p.h"
|
||||||
#include "cdbsymbolgroupcontext.h"
|
|
||||||
#include "cdbstacktracecontext.h"
|
#include "cdbstacktracecontext.h"
|
||||||
|
#include "cdbstackframecontext.h"
|
||||||
|
#include "cdbsymbolgroupcontext.h"
|
||||||
#include "cdbbreakpoint.h"
|
#include "cdbbreakpoint.h"
|
||||||
#include "cdbmodules.h"
|
#include "cdbmodules.h"
|
||||||
#include "cdbassembler.h"
|
#include "cdbassembler.h"
|
||||||
@@ -274,7 +275,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent,
|
|||||||
m_hDebuggeeProcess(0),
|
m_hDebuggeeProcess(0),
|
||||||
m_hDebuggeeThread(0),
|
m_hDebuggeeThread(0),
|
||||||
m_breakEventMode(BreakEventHandle),
|
m_breakEventMode(BreakEventHandle),
|
||||||
m_dumper(&m_cif),
|
m_dumper(new CdbDumperHelper(parent, &m_cif)),
|
||||||
m_watchTimer(-1),
|
m_watchTimer(-1),
|
||||||
m_debugEventCallBack(engine),
|
m_debugEventCallBack(engine),
|
||||||
m_engine(engine),
|
m_engine(engine),
|
||||||
@@ -464,7 +465,7 @@ bool CdbDebugEngine::startDebugger()
|
|||||||
dumperEnabled = false;
|
dumperEnabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_d->m_dumper.reset(dumperLibName, dumperEnabled);
|
m_d->m_dumper->reset(dumperLibName, dumperEnabled);
|
||||||
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
|
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
bool rc = false;
|
bool rc = false;
|
||||||
@@ -590,19 +591,20 @@ void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG6
|
|||||||
} else {
|
} else {
|
||||||
m_currentThreadId = 0;
|
m_currentThreadId = 0;
|
||||||
}
|
}
|
||||||
// Set initial breakpoints
|
// Clear any saved breakpoints and set initial breakpoints
|
||||||
|
m_engine->executeDebuggerCommand(QLatin1String("bc"));
|
||||||
if (m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints())
|
if (m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints())
|
||||||
m_engine->attemptBreakpointSynchronization();
|
m_engine->attemptBreakpointSynchronization();
|
||||||
// At any event, we want a temporary breakpoint at main() to load
|
// At any event, we want a temporary breakpoint at main() to load
|
||||||
// the dumpers.
|
// the dumpers.
|
||||||
if (m_dumper.state() == CdbDumperHelper::NotLoaded) {
|
if (m_dumper->state() == CdbDumperHelper::NotLoaded) {
|
||||||
if (!hasBreakPointAtMain(m_debuggerManagerAccess->breakHandler())) {
|
if (!hasBreakPointAtMain(m_debuggerManagerAccess->breakHandler())) {
|
||||||
|
QString errorMessage;
|
||||||
CDBBreakPoint mainBP;
|
CDBBreakPoint mainBP;
|
||||||
// Do not resolve at this point in the rare event someone
|
// Do not resolve at this point in the rare event someone
|
||||||
// has main in a module
|
// has main in a module
|
||||||
mainBP.funcName = QLatin1String("main");
|
mainBP.funcName = QLatin1String("main");
|
||||||
mainBP.oneShot = true;
|
mainBP.oneShot = true;
|
||||||
QString errorMessage;
|
|
||||||
if (!mainBP.add(m_cif.debugControl, &errorMessage))
|
if (!mainBP.add(m_cif.debugControl, &errorMessage))
|
||||||
m_debuggerManagerAccess->showQtDumperLibraryWarning(errorMessage);
|
m_debuggerManagerAccess->showQtDumperLibraryWarning(errorMessage);
|
||||||
}
|
}
|
||||||
@@ -675,13 +677,13 @@ void CdbDebugEngine::exitDebugger()
|
|||||||
killWatchTimer();
|
killWatchTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
CdbSymbolGroupContext *CdbDebugEnginePrivate::getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const
|
CdbStackFrameContext *CdbDebugEnginePrivate::getStackFrameContext(int frameIndex, QString *errorMessage) const
|
||||||
{
|
{
|
||||||
if (!m_currentStackTrace) {
|
if (!m_currentStackTrace) {
|
||||||
*errorMessage = QLatin1String(msgNoStackTraceC);
|
*errorMessage = QLatin1String(msgNoStackTraceC);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (CdbSymbolGroupContext *sg = m_currentStackTrace->symbolGroupContextAt(frameIndex, errorMessage))
|
if (CdbStackFrameContext *sg = m_currentStackTrace->frameContextAt(frameIndex, errorMessage))
|
||||||
return sg;
|
return sg;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -718,8 +720,8 @@ bool CdbDebugEnginePrivate::updateLocals(int frameIndex,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (CdbSymbolGroupContext *sgc = getStackFrameSymbolGroupContext(frameIndex, errorMessage))
|
if (CdbStackFrameContext *sgc = getStackFrameContext(frameIndex, errorMessage))
|
||||||
success = CdbSymbolGroupContext::populateModelInitially(sgc, wh, errorMessage);
|
success = sgc->populateModelInitially(wh, errorMessage);
|
||||||
|
|
||||||
wh->rebuildModel();
|
wh->rebuildModel();
|
||||||
return success;
|
return success;
|
||||||
@@ -800,8 +802,8 @@ void CdbDebugEngine::updateWatchModel()
|
|||||||
filterEvaluateWatchers(&incomplete, watchHandler);
|
filterEvaluateWatchers(&incomplete, watchHandler);
|
||||||
// Do locals. We might get called while running when someone enters watchers
|
// Do locals. We might get called while running when someone enters watchers
|
||||||
if (!incomplete.empty()) {
|
if (!incomplete.empty()) {
|
||||||
CdbSymbolGroupContext *sg = m_d->m_currentStackTrace->symbolGroupContextAt(frameIndex, &errorMessage);
|
CdbStackFrameContext *sg = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
|
||||||
if (!sg || !CdbSymbolGroupContext::completeModel(sg, incomplete, watchHandler, &errorMessage))
|
if (!sg || !sg->completeModel(incomplete, watchHandler, &errorMessage))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
watchHandler->rebuildModel();
|
watchHandler->rebuildModel();
|
||||||
@@ -1016,7 +1018,7 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v
|
|||||||
bool success = false;
|
bool success = false;
|
||||||
do {
|
do {
|
||||||
QString newValue;
|
QString newValue;
|
||||||
CdbSymbolGroupContext *sg = m_d->getStackFrameSymbolGroupContext(frameIndex, &errorMessage);
|
CdbStackFrameContext *sg = m_d->getStackFrameContext(frameIndex, &errorMessage);
|
||||||
if (!sg)
|
if (!sg)
|
||||||
break;
|
break;
|
||||||
if (!sg->assignValue(expr, value, &newValue, &errorMessage))
|
if (!sg->assignValue(expr, value, &newValue, &errorMessage))
|
||||||
@@ -1061,17 +1063,30 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression,
|
|||||||
QString *value,
|
QString *value,
|
||||||
QString *type,
|
QString *type,
|
||||||
QString *errorMessage)
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
DEBUG_VALUE debugValue;
|
||||||
|
if (!m_d->evaluateExpression(m_d->m_cif.debugControl, expression, &debugValue, errorMessage))
|
||||||
|
return false;
|
||||||
|
*value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CdbDebugEnginePrivate::evaluateExpression(CIDebugControl *ctrl,
|
||||||
|
const QString &expression,
|
||||||
|
DEBUG_VALUE *debugValue,
|
||||||
|
QString *errorMessage)
|
||||||
{
|
{
|
||||||
if (debugCDB > 1)
|
if (debugCDB > 1)
|
||||||
qDebug() << Q_FUNC_INFO << expression;
|
qDebug() << Q_FUNC_INFO << expression;
|
||||||
DEBUG_VALUE debugValue;
|
|
||||||
memset(&debugValue, 0, sizeof(DEBUG_VALUE));
|
memset(debugValue, 0, sizeof(DEBUG_VALUE));
|
||||||
// Original syntax must be restored, else setting breakpoints will fail.
|
// Original syntax must be restored, else setting breakpoints will fail.
|
||||||
SyntaxSetter syntaxSetter(m_d->m_cif.debugControl, DEBUG_EXPR_CPLUSPLUS);
|
SyntaxSetter syntaxSetter(ctrl, DEBUG_EXPR_CPLUSPLUS);
|
||||||
ULONG errorPosition = 0;
|
ULONG errorPosition = 0;
|
||||||
const HRESULT hr = m_d->m_cif.debugControl->EvaluateWide(expression.utf16(),
|
const HRESULT hr = ctrl->EvaluateWide(expression.utf16(),
|
||||||
DEBUG_VALUE_INVALID, &debugValue,
|
DEBUG_VALUE_INVALID, debugValue,
|
||||||
&errorPosition); if (FAILED(hr)) {
|
&errorPosition);
|
||||||
|
if (FAILED(hr)) {
|
||||||
if (HRESULT_CODE(hr) == 517) {
|
if (HRESULT_CODE(hr) == 517) {
|
||||||
*errorMessage = QString::fromLatin1("Unable to evaluate '%1': Expression out of scope.").
|
*errorMessage = QString::fromLatin1("Unable to evaluate '%1': Expression out of scope.").
|
||||||
arg(expression);
|
arg(expression);
|
||||||
@@ -1081,7 +1096,6 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression,
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1355,14 +1369,14 @@ void CdbDebugEnginePrivate::handleDebugEvent()
|
|||||||
case BreakEventHandle:
|
case BreakEventHandle:
|
||||||
case BreakEventMain:
|
case BreakEventMain:
|
||||||
if (mode == BreakEventMain)
|
if (mode == BreakEventMain)
|
||||||
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
|
m_dumper->load(m_debuggerManager);
|
||||||
m_debuggerManagerAccess->notifyInferiorStopped();
|
m_debuggerManagerAccess->notifyInferiorStopped();
|
||||||
updateThreadList();
|
updateThreadList();
|
||||||
updateStackTrace();
|
updateStackTrace();
|
||||||
break;
|
break;
|
||||||
case BreakEventMainLoadDumpers:
|
case BreakEventMainLoadDumpers:
|
||||||
// Temp stop to load dumpers
|
// Temp stop to load dumpers
|
||||||
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
|
m_dumper->load(m_debuggerManager);
|
||||||
m_engine->startWatchTimer();
|
m_engine->startWatchTimer();
|
||||||
continueInferiorProcess();
|
continueInferiorProcess();
|
||||||
break;
|
break;
|
||||||
@@ -1436,7 +1450,7 @@ void CdbDebugEnginePrivate::updateStackTrace()
|
|||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
m_engine->reloadRegisters();
|
m_engine->reloadRegisters();
|
||||||
m_currentStackTrace =
|
m_currentStackTrace =
|
||||||
CdbStackTraceContext::create(&m_cif, m_currentThreadId, &errorMessage);
|
CdbStackTraceContext::create(m_dumper, m_currentThreadId, &errorMessage);
|
||||||
if (!m_currentStackTrace) {
|
if (!m_currentStackTrace) {
|
||||||
qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage));
|
qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage));
|
||||||
return;
|
return;
|
||||||
|
@@ -46,7 +46,7 @@ namespace Internal {
|
|||||||
class DebuggerManager;
|
class DebuggerManager;
|
||||||
class IDebuggerManagerAccessForEngines;
|
class IDebuggerManagerAccessForEngines;
|
||||||
class WatchHandler;
|
class WatchHandler;
|
||||||
class CdbSymbolGroupContext;
|
class CdbStackFrameContext;
|
||||||
class CdbStackTraceContext;
|
class CdbStackTraceContext;
|
||||||
|
|
||||||
// Thin wrapper around the 'DBEng' debugger engine shared library
|
// Thin wrapper around the 'DBEng' debugger engine shared library
|
||||||
@@ -125,7 +125,7 @@ struct CdbDebugEnginePrivate
|
|||||||
void cleanStackTrace();
|
void cleanStackTrace();
|
||||||
void clearForRun();
|
void clearForRun();
|
||||||
void handleModuleLoad(const QString &);
|
void handleModuleLoad(const QString &);
|
||||||
CdbSymbolGroupContext *getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const;
|
CdbStackFrameContext *getStackFrameContext(int frameIndex, QString *errorMessage) const;
|
||||||
void clearDisplay();
|
void clearDisplay();
|
||||||
|
|
||||||
bool interruptInterferiorProcess(QString *errorMessage);
|
bool interruptInterferiorProcess(QString *errorMessage);
|
||||||
@@ -136,6 +136,7 @@ struct CdbDebugEnginePrivate
|
|||||||
bool attemptBreakpointSynchronization(QString *errorMessage);
|
bool attemptBreakpointSynchronization(QString *errorMessage);
|
||||||
|
|
||||||
static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage);
|
static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage);
|
||||||
|
static bool evaluateExpression(CIDebugControl *ctrl, const QString &expression, DEBUG_VALUE *v, QString *errorMessage);
|
||||||
|
|
||||||
const QSharedPointer<CdbOptions> m_options;
|
const QSharedPointer<CdbOptions> m_options;
|
||||||
HANDLE m_hDebuggeeProcess;
|
HANDLE m_hDebuggeeProcess;
|
||||||
@@ -147,7 +148,7 @@ struct CdbDebugEnginePrivate
|
|||||||
CdbComInterfaces m_cif;
|
CdbComInterfaces m_cif;
|
||||||
CdbDebugEventCallback m_debugEventCallBack;
|
CdbDebugEventCallback m_debugEventCallBack;
|
||||||
CdbDebugOutput m_debugOutputCallBack;
|
CdbDebugOutput m_debugOutputCallBack;
|
||||||
CdbDumperHelper m_dumper;
|
QSharedPointer<CdbDumperHelper> m_dumper;
|
||||||
|
|
||||||
CdbDebugEngine* m_engine;
|
CdbDebugEngine* m_engine;
|
||||||
DebuggerManager *m_debuggerManager;
|
DebuggerManager *m_debuggerManager;
|
||||||
|
@@ -318,14 +318,16 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Format exception with stacktrace in case of C++ exception
|
// Format exception with stacktrace in case of C++ exception
|
||||||
void formatException(const EXCEPTION_RECORD64 *e, CdbComInterfaces &cif, QTextStream &str)
|
void formatException(const EXCEPTION_RECORD64 *e,
|
||||||
|
const QSharedPointer<CdbDumperHelper> &dumper,
|
||||||
|
QTextStream &str)
|
||||||
{
|
{
|
||||||
formatException(e, str);
|
formatException(e, str);
|
||||||
if (e->ExceptionCode == cppExceptionCode) {
|
if (e->ExceptionCode == cppExceptionCode) {
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
ULONG currentThreadId = 0;
|
ULONG currentThreadId = 0;
|
||||||
cif.debugSystemObjects->GetCurrentThreadId(¤tThreadId);
|
dumper->comInterfaces()->debugSystemObjects->GetCurrentThreadId(¤tThreadId);
|
||||||
if (CdbStackTraceContext *stc = CdbStackTraceContext::create(&cif, currentThreadId, &errorMessage)) {
|
if (CdbStackTraceContext *stc = CdbStackTraceContext::create(dumper, currentThreadId, &errorMessage)) {
|
||||||
str << "at:\n";
|
str << "at:\n";
|
||||||
stc->format(str);
|
stc->format(str);
|
||||||
str <<'\n';
|
str <<'\n';
|
||||||
@@ -343,7 +345,7 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
|
|||||||
QString msg;
|
QString msg;
|
||||||
{
|
{
|
||||||
QTextStream str(&msg);
|
QTextStream str(&msg);
|
||||||
formatException(Exception, m_pEngine->m_d->m_cif, str);
|
formatException(Exception, m_pEngine->m_d->m_dumper, str);
|
||||||
}
|
}
|
||||||
if (debugCDB)
|
if (debugCDB)
|
||||||
qDebug() << Q_FUNC_INFO << '\n' << msg;
|
qDebug() << Q_FUNC_INFO << '\n' << msg;
|
||||||
@@ -469,6 +471,37 @@ STDMETHODIMP CdbDebugEventCallback::SystemError(
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------ExceptionLoggerEventCallback
|
||||||
|
CdbExceptionLoggerEventCallback::CdbExceptionLoggerEventCallback(const QString &logPrefix,
|
||||||
|
IDebuggerManagerAccessForEngines *access) :
|
||||||
|
m_logPrefix(logPrefix),
|
||||||
|
m_access(access)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP CdbExceptionLoggerEventCallback::GetInterestMask(THIS_ __out PULONG mask)
|
||||||
|
{
|
||||||
|
*mask = DEBUG_EVENT_EXCEPTION;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP CdbExceptionLoggerEventCallback::Exception(
|
||||||
|
THIS_
|
||||||
|
__in PEXCEPTION_RECORD64 Exception,
|
||||||
|
__in ULONG /* FirstChance */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
m_exceptionMessages.push_back(QString());
|
||||||
|
{
|
||||||
|
QTextStream str(&m_exceptionMessages.back());
|
||||||
|
formatException(Exception, str);
|
||||||
|
}
|
||||||
|
if (debugCDB)
|
||||||
|
qDebug() << Q_FUNC_INFO << '\n' << m_exceptionMessages.back();
|
||||||
|
m_access->showDebuggerOutput(m_logPrefix, m_exceptionMessages.back());
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------IgnoreDebugEventCallback
|
// -----------IgnoreDebugEventCallback
|
||||||
IgnoreDebugEventCallback::IgnoreDebugEventCallback()
|
IgnoreDebugEventCallback::IgnoreDebugEventCallback()
|
||||||
{
|
{
|
||||||
|
@@ -32,12 +32,13 @@
|
|||||||
|
|
||||||
#include "cdbcom.h"
|
#include "cdbcom.h"
|
||||||
|
|
||||||
#include <QtCore/QtGlobal>
|
#include <QtCore/QStringList>
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class CdbDebugEngine;
|
class CdbDebugEngine;
|
||||||
|
class IDebuggerManagerAccessForEngines;
|
||||||
|
|
||||||
// Base class for event callbacks that takes care
|
// Base class for event callbacks that takes care
|
||||||
// Active X magic. Provides base implementations with
|
// Active X magic. Provides base implementations with
|
||||||
@@ -235,6 +236,34 @@ private:
|
|||||||
CdbDebugEngine *m_pEngine;
|
CdbDebugEngine *m_pEngine;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Event handler logs exceptions to the debugger window
|
||||||
|
// and ignores the rest. To be used for running dumper calls.
|
||||||
|
class CdbExceptionLoggerEventCallback : public CdbDebugEventCallbackBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CdbExceptionLoggerEventCallback(const QString &logPrefix,
|
||||||
|
IDebuggerManagerAccessForEngines *access);
|
||||||
|
|
||||||
|
STDMETHOD(GetInterestMask)(
|
||||||
|
THIS_
|
||||||
|
__out PULONG mask
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(Exception)(
|
||||||
|
THIS_
|
||||||
|
__in PEXCEPTION_RECORD64 Exception,
|
||||||
|
__in ULONG FirstChance
|
||||||
|
);
|
||||||
|
|
||||||
|
int exceptionCount() const { return m_exceptionMessages.size(); }
|
||||||
|
QStringList exceptionMessages() const { return m_exceptionMessages; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString m_logPrefix;
|
||||||
|
IDebuggerManagerAccessForEngines *m_access;
|
||||||
|
QStringList m_exceptionMessages;
|
||||||
|
};
|
||||||
|
|
||||||
// Event handler that ignores everything
|
// Event handler that ignores everything
|
||||||
class IgnoreDebugEventCallback : public CdbDebugEventCallbackBase
|
class IgnoreDebugEventCallback : public CdbDebugEventCallbackBase
|
||||||
{
|
{
|
||||||
|
@@ -80,7 +80,8 @@ STDMETHODIMP CdbDebugOutputBase::Output(
|
|||||||
IN PCWSTR text
|
IN PCWSTR text
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
output(mask, QString::fromUtf16(text));
|
const QString msg = QString::fromUtf16(text);
|
||||||
|
output(mask, msg.trimmed());
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,7 +32,8 @@
|
|||||||
#include "cdbdebugengine_p.h"
|
#include "cdbdebugengine_p.h"
|
||||||
#include "cdbdebugoutput.h"
|
#include "cdbdebugoutput.h"
|
||||||
#include "cdbdebugeventcallback.h"
|
#include "cdbdebugeventcallback.h"
|
||||||
#include "watchutils.h"
|
#include "cdbsymbolgroupcontext.h"
|
||||||
|
#include "watchhandler.h"
|
||||||
|
|
||||||
#include <QtCore/QRegExp>
|
#include <QtCore/QRegExp>
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
@@ -43,21 +44,20 @@ enum { loadDebug = 0 };
|
|||||||
static const char *dumperModuleNameC = "gdbmacros";
|
static const char *dumperModuleNameC = "gdbmacros";
|
||||||
static const char *qtCoreModuleNameC = "QtCore";
|
static const char *qtCoreModuleNameC = "QtCore";
|
||||||
static const ULONG waitTimeOutMS = 30000;
|
static const ULONG waitTimeOutMS = 30000;
|
||||||
|
static const char *dumperPrefixC = "dumper:";
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
// Alloc memory in debuggee using the ".dvalloc" command as
|
// Alloc memory in debuggee using the ".dvalloc" command as
|
||||||
// there seems to be no API for it.
|
// there seems to be no API for it.
|
||||||
static bool allocDebuggeeMemory(CIDebugControl *ctl,
|
static bool allocDebuggeeMemory(CdbComInterfaces *cif,
|
||||||
CIDebugClient *client,
|
|
||||||
int size, ULONG64 *address, QString *errorMessage)
|
int size, ULONG64 *address, QString *errorMessage)
|
||||||
{
|
{
|
||||||
*address = 0;
|
*address = 0;
|
||||||
const QString allocCmd = QLatin1String(".dvalloc ") + QString::number(size);
|
const QString allocCmd = QLatin1String(".dvalloc ") + QString::number(size);
|
||||||
StringOutputHandler stringHandler;
|
StringOutputHandler stringHandler;
|
||||||
OutputRedirector redir(client, &stringHandler);
|
OutputRedirector redir(cif->debugClient, &stringHandler);
|
||||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, allocCmd, errorMessage))
|
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, allocCmd, errorMessage))
|
||||||
return false;
|
return false;
|
||||||
// "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
|
// "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
@@ -68,7 +68,7 @@ static bool allocDebuggeeMemory(CIDebugControl *ctl,
|
|||||||
if (ok)
|
if (ok)
|
||||||
*address = addri;
|
*address = addri;
|
||||||
}
|
}
|
||||||
if (loadDebug)
|
if (loadDebug > 1)
|
||||||
qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok;
|
qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok;
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
*errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output);
|
*errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output);
|
||||||
@@ -78,18 +78,16 @@ static bool allocDebuggeeMemory(CIDebugControl *ctl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alloc an AscII string in debuggee
|
// Alloc an AscII string in debuggee
|
||||||
static bool createDebuggeeAscIIString(CIDebugControl *ctl,
|
static bool createDebuggeeAscIIString(CdbComInterfaces *cif,
|
||||||
CIDebugClient *client,
|
|
||||||
CIDebugDataSpaces *data,
|
|
||||||
const QString &s,
|
const QString &s,
|
||||||
ULONG64 *address,
|
ULONG64 *address,
|
||||||
QString *errorMessage)
|
QString *errorMessage)
|
||||||
{
|
{
|
||||||
QByteArray sAsciiData = s.toLocal8Bit();
|
QByteArray sAsciiData = s.toLocal8Bit();
|
||||||
sAsciiData += '\0';
|
sAsciiData += '\0';
|
||||||
if (!allocDebuggeeMemory(ctl, client, sAsciiData.size(), address, errorMessage))
|
if (!allocDebuggeeMemory(cif, sAsciiData.size(), address, errorMessage))
|
||||||
return false;
|
return false;
|
||||||
const HRESULT hr = data->WriteVirtual(*address, sAsciiData.data(), sAsciiData.size(), 0);
|
const HRESULT hr = cif->debugDataSpaces->WriteVirtual(*address, sAsciiData.data(), sAsciiData.size(), 0);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage= msgComFailed("WriteVirtual", hr);
|
*errorMessage= msgComFailed("WriteVirtual", hr);
|
||||||
return false;
|
return false;
|
||||||
@@ -101,21 +99,20 @@ static bool createDebuggeeAscIIString(CIDebugControl *ctl,
|
|||||||
// the QtCored4.pdb file to be present as we need "qstrdup"
|
// the QtCored4.pdb file to be present as we need "qstrdup"
|
||||||
// as dummy symbol. This is ok ATM since dumpers only
|
// as dummy symbol. This is ok ATM since dumpers only
|
||||||
// make sense for Qt apps.
|
// make sense for Qt apps.
|
||||||
static bool debuggeeLoadLibrary(CIDebugControl *ctl,
|
static bool debuggeeLoadLibrary(IDebuggerManagerAccessForEngines *access,
|
||||||
CIDebugClient *client,
|
CdbComInterfaces *cif,
|
||||||
CIDebugSymbols *syms,
|
const QString &moduleName,
|
||||||
CIDebugDataSpaces *data,
|
QString *errorMessage)
|
||||||
const QString &moduleName, QString *errorMessage)
|
|
||||||
{
|
{
|
||||||
if (loadDebug)
|
if (loadDebug > 1)
|
||||||
qDebug() << Q_FUNC_INFO << moduleName;
|
qDebug() << Q_FUNC_INFO << moduleName;
|
||||||
// Try to ignore the breakpoints
|
// Try to ignore the breakpoints
|
||||||
IgnoreDebugEventCallback devNull;
|
CdbExceptionLoggerEventCallback exLogger(QLatin1String(dumperPrefixC), access);
|
||||||
EventCallbackRedirector eventRedir(client, &devNull);
|
EventCallbackRedirector eventRedir(cif->debugClient, &exLogger);
|
||||||
// Make a call to LoadLibraryA. First, reserve memory in debugger
|
// Make a call to LoadLibraryA. First, reserve memory in debugger
|
||||||
// and copy name over.
|
// and copy name over.
|
||||||
ULONG64 nameAddress;
|
ULONG64 nameAddress;
|
||||||
if (!createDebuggeeAscIIString(ctl, client, data, moduleName, &nameAddress, errorMessage))
|
if (!createDebuggeeAscIIString(cif, moduleName, &nameAddress, errorMessage))
|
||||||
return false;
|
return false;
|
||||||
// We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)"
|
// We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)"
|
||||||
// (void* LoadLibraryA(char*)). However, despite providing a symbol
|
// (void* LoadLibraryA(char*)). However, despite providing a symbol
|
||||||
@@ -125,21 +122,21 @@ static bool debuggeeLoadLibrary(CIDebugControl *ctl,
|
|||||||
// Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some
|
// Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some
|
||||||
// reason, the symbol is present in QtGui as well without type information.
|
// reason, the symbol is present in QtGui as well without type information.
|
||||||
QString dummyFunc = QLatin1String("*qstrdup");
|
QString dummyFunc = QLatin1String("*qstrdup");
|
||||||
if (resolveSymbol(syms, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
|
if (resolveSymbol(cif->debugSymbols, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
|
||||||
return false;
|
return false;
|
||||||
QString callCmd = QLatin1String(".call ");
|
QString callCmd = QLatin1String(".call ");
|
||||||
callCmd += dummyFunc;
|
callCmd += dummyFunc;
|
||||||
callCmd += QLatin1String("(0x");
|
callCmd += QLatin1String("(0x");
|
||||||
callCmd += QString::number(nameAddress, 16);
|
callCmd += QString::number(nameAddress, 16);
|
||||||
callCmd += QLatin1Char(')');
|
callCmd += QLatin1Char(')');
|
||||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, callCmd, errorMessage))
|
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, callCmd, errorMessage))
|
||||||
return false;
|
return false;
|
||||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QLatin1String("r eip=Kernel32!LoadLibraryA"), errorMessage))
|
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, QLatin1String("r eip=Kernel32!LoadLibraryA"), errorMessage))
|
||||||
return false;
|
return false;
|
||||||
// This will hit a breakpoint.
|
// This will hit a breakpoint.
|
||||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QString(QLatin1Char('g')), errorMessage))
|
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, QString(QLatin1Char('g')), errorMessage))
|
||||||
return false;
|
return false;
|
||||||
const HRESULT hr = ctl->WaitForEvent(0, waitTimeOutMS);
|
const HRESULT hr = cif->debugControl->WaitForEvent(0, waitTimeOutMS);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("WaitForEvent", hr);
|
*errorMessage = msgComFailed("WaitForEvent", hr);
|
||||||
return false;
|
return false;
|
||||||
@@ -147,66 +144,13 @@ static bool debuggeeLoadLibrary(CIDebugControl *ctl,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------- CdbDumperHelper::DumperInputParameters
|
|
||||||
struct CdbDumperHelper::DumperInputParameters {
|
|
||||||
DumperInputParameters(int protocolVersion_ = 1,
|
|
||||||
int token_ = 1,
|
|
||||||
quint64 Address_ = 0,
|
|
||||||
bool dumpChildren_ = false,
|
|
||||||
int extraInt0_ = 0,
|
|
||||||
int extraInt1_ = 0,
|
|
||||||
int extraInt2_ = 0,
|
|
||||||
int extraInt3_ = 0);
|
|
||||||
|
|
||||||
QString command(const QString &dumpFunction) const;
|
|
||||||
|
|
||||||
int protocolVersion;
|
|
||||||
int token;
|
|
||||||
quint64 address;
|
|
||||||
bool dumpChildren;
|
|
||||||
int extraInt0;
|
|
||||||
int extraInt1;
|
|
||||||
int extraInt2;
|
|
||||||
int extraInt3;
|
|
||||||
};
|
|
||||||
|
|
||||||
CdbDumperHelper::DumperInputParameters::DumperInputParameters(int protocolVersion_,
|
|
||||||
int token_,
|
|
||||||
quint64 address_,
|
|
||||||
bool dumpChildren_,
|
|
||||||
int extraInt0_,
|
|
||||||
int extraInt1_,
|
|
||||||
int extraInt2_,
|
|
||||||
int extraInt3_) :
|
|
||||||
protocolVersion(protocolVersion_),
|
|
||||||
token(token_),
|
|
||||||
address(address_),
|
|
||||||
dumpChildren(dumpChildren_),
|
|
||||||
extraInt0(extraInt0_),
|
|
||||||
extraInt1(extraInt1_),
|
|
||||||
extraInt2(extraInt2_),
|
|
||||||
extraInt3(extraInt3_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString CdbDumperHelper::DumperInputParameters::command(const QString &dumpFunction) const
|
|
||||||
{
|
|
||||||
QString rc;
|
|
||||||
QTextStream str(&rc);
|
|
||||||
str.setIntegerBase(16);
|
|
||||||
str << ".call " << dumpFunction << '(' << protocolVersion << ',' << token << ',';
|
|
||||||
str.setIntegerBase(16);
|
|
||||||
str << "0x" << address;
|
|
||||||
str.setIntegerBase(10);
|
|
||||||
str << ',' << (dumpChildren ? 1 : 0) << ',' << extraInt0 << ',' << extraInt1
|
|
||||||
<< ',' << extraInt2 << ',' << extraInt3 << ')';
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------- CdbDumperHelper
|
// ------------------- CdbDumperHelper
|
||||||
|
|
||||||
CdbDumperHelper::CdbDumperHelper(CdbComInterfaces *cif) :
|
CdbDumperHelper::CdbDumperHelper(IDebuggerManagerAccessForEngines *access,
|
||||||
|
CdbComInterfaces *cif) :
|
||||||
|
m_messagePrefix(QLatin1String(dumperPrefixC)),
|
||||||
m_state(NotLoaded),
|
m_state(NotLoaded),
|
||||||
|
m_access(access),
|
||||||
m_cif(cif),
|
m_cif(cif),
|
||||||
m_inBufferAddress(0),
|
m_inBufferAddress(0),
|
||||||
m_inBufferSize(0),
|
m_inBufferSize(0),
|
||||||
@@ -234,24 +178,23 @@ void CdbDumperHelper::reset(const QString &library, bool enabled)
|
|||||||
m_library = library;
|
m_library = library;
|
||||||
m_state = enabled ? NotLoaded : Disabled;
|
m_state = enabled ? NotLoaded : Disabled;
|
||||||
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
|
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
|
||||||
m_knownTypes.clear();
|
m_helper.clear();
|
||||||
m_qtVersion.clear();
|
|
||||||
m_qtNamespace.clear();
|
|
||||||
m_inBufferAddress = m_outBufferAddress = 0;
|
m_inBufferAddress = m_outBufferAddress = 0;
|
||||||
m_inBufferSize = m_outBufferSize = 0;
|
m_inBufferSize = m_outBufferSize = 0;
|
||||||
|
m_typeSizeCache.clear();
|
||||||
|
m_failedTypes.clear();
|
||||||
clearBuffer();
|
clearBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to load and initialize dumpers, give feedback
|
// Attempt to load and initialize dumpers, give feedback
|
||||||
// to user.
|
// to user.
|
||||||
void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEngines *access)
|
void CdbDumperHelper::load(DebuggerManager *manager)
|
||||||
{
|
{
|
||||||
enum Result { Failed, Succeeded, NoQtApp };
|
enum Result { Failed, Succeeded, NoQtApp };
|
||||||
|
|
||||||
if (m_state != NotLoaded)
|
if (m_state != NotLoaded)
|
||||||
return;
|
return;
|
||||||
manager->showStatusMessage(QCoreApplication::translate("CdbDumperHelper", "Loading dumpers..."), 10000);
|
manager->showStatusMessage(QCoreApplication::translate("CdbDumperHelper", "Loading dumpers..."), 10000);
|
||||||
const QString messagePrefix = QLatin1String("dumper:");
|
|
||||||
QString message;
|
QString message;
|
||||||
Result result = Failed;
|
Result result = Failed;
|
||||||
do {
|
do {
|
||||||
@@ -266,12 +209,11 @@ void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEn
|
|||||||
// Make sure the dumper lib is loaded.
|
// Make sure the dumper lib is loaded.
|
||||||
if (modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty()) {
|
if (modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty()) {
|
||||||
// Try to load
|
// Try to load
|
||||||
if (!debuggeeLoadLibrary(m_cif->debugControl, m_cif->debugClient, m_cif->debugSymbols, m_cif->debugDataSpaces,
|
if (!debuggeeLoadLibrary(m_access, m_cif, m_library, &message)) {
|
||||||
m_library, &message)) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
access->showDebuggerOutput(messagePrefix, QCoreApplication::translate("CdbDumperHelper", "The dumper module appears to be already loaded."));
|
m_access->showDebuggerOutput(m_messagePrefix, QCoreApplication::translate("CdbDumperHelper", "The dumper module appears to be already loaded."));
|
||||||
}
|
}
|
||||||
// Resolve symbols and do call to get types
|
// Resolve symbols and do call to get types
|
||||||
if (!resolveSymbols(&message))
|
if (!resolveSymbols(&message))
|
||||||
@@ -285,17 +227,17 @@ void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEn
|
|||||||
switch (result) {
|
switch (result) {
|
||||||
case Failed:
|
case Failed:
|
||||||
message = QCoreApplication::translate("CdbDumperHelper", "The dumper library '%1' could not be loaded:\n%2").arg(m_library, message);
|
message = QCoreApplication::translate("CdbDumperHelper", "The dumper library '%1' could not be loaded:\n%2").arg(m_library, message);
|
||||||
access->showDebuggerOutput(messagePrefix, message);
|
m_access->showDebuggerOutput(m_messagePrefix, message);
|
||||||
access->showQtDumperLibraryWarning(message);
|
m_access->showQtDumperLibraryWarning(message);
|
||||||
m_state = Disabled;
|
m_state = Disabled;
|
||||||
break;
|
break;
|
||||||
case Succeeded:
|
case Succeeded:
|
||||||
access->showDebuggerOutput(messagePrefix, message);
|
m_access->showDebuggerOutput(m_messagePrefix, message);
|
||||||
access->showDebuggerOutput(messagePrefix, statusMessage());
|
m_access->showDebuggerOutput(m_messagePrefix, m_helper.toString());
|
||||||
m_state = Loaded;
|
m_state = Loaded;
|
||||||
break;
|
break;
|
||||||
case NoQtApp:
|
case NoQtApp:
|
||||||
access->showDebuggerOutput(messagePrefix, message);
|
m_access->showDebuggerOutput(m_messagePrefix, message);
|
||||||
m_state = Disabled;
|
m_state = Disabled;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -303,15 +245,6 @@ void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEn
|
|||||||
qDebug() << Q_FUNC_INFO << "\n<" << result << '>' << m_state << message;
|
qDebug() << Q_FUNC_INFO << "\n<" << result << '>' << m_state << message;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CdbDumperHelper::statusMessage() const
|
|
||||||
{
|
|
||||||
const QString nameSpace = m_qtNamespace.isEmpty() ? QCoreApplication::translate("CdbDumperHelper", "<none>") : m_qtNamespace;
|
|
||||||
return QCoreApplication::translate("CdbDumperHelper",
|
|
||||||
"%n known types, Qt version: %1, Qt namespace: %2",
|
|
||||||
0, QCoreApplication::CodecForTr,
|
|
||||||
m_knownTypes.size()).arg(m_qtVersion, nameSpace);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve address and optionally size of a symbol.
|
// Retrieve address and optionally size of a symbol.
|
||||||
static inline bool getSymbolAddress(CIDebugSymbols *sg,
|
static inline bool getSymbolAddress(CIDebugSymbols *sg,
|
||||||
const QString &name,
|
const QString &name,
|
||||||
@@ -373,35 +306,76 @@ bool CdbDumperHelper::resolveSymbols(QString *errorMessage)
|
|||||||
bool CdbDumperHelper::getKnownTypes(QString *errorMessage)
|
bool CdbDumperHelper::getKnownTypes(QString *errorMessage)
|
||||||
{
|
{
|
||||||
QByteArray output;
|
QByteArray output;
|
||||||
if (!callDumper(DumperInputParameters(1), &output, errorMessage)) {
|
QString callCmd;
|
||||||
|
QTextStream(&callCmd) << ".call " << m_dumpObjectSymbol << "(1,0,0,0,0,0,0,0)";
|
||||||
|
const char *outData;
|
||||||
|
if (!callDumper(callCmd, QByteArray(), &outData, errorMessage)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!parseQueryDumperOutput(output, &m_knownTypes, &m_qtVersion, &m_qtNamespace)) {
|
if (!m_helper.parseQuery(outData, QtDumperHelper::CdbDebugger)) {
|
||||||
*errorMessage = QString::fromLatin1("Unable to parse the dumper output: '%1'").arg(QString::fromAscii(output));
|
*errorMessage = QString::fromLatin1("Unable to parse the dumper output: '%1'").arg(QString::fromAscii(output));
|
||||||
}
|
}
|
||||||
if (loadDebug)
|
if (loadDebug)
|
||||||
qDebug() << Q_FUNC_INFO << m_knownTypes << m_qtVersion << m_qtNamespace;
|
qDebug() << Q_FUNC_INFO << m_helper.toString(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CdbDumperHelper::callDumper(const DumperInputParameters &p, QByteArray *output, QString *errorMessage)
|
// Write to debuggee memory in chunks
|
||||||
|
bool CdbDumperHelper::writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &buffer, quint64 address, QString *errorMessage)
|
||||||
{
|
{
|
||||||
IgnoreDebugEventCallback devNull;
|
char *ptr = const_cast<char*>(buffer.data());
|
||||||
EventCallbackRedirector eventRedir(m_cif->debugClient, &devNull);
|
ULONG bytesToWrite = buffer.size();
|
||||||
const QString callCmd = p.command(m_dumpObjectSymbol);
|
while (bytesToWrite > 0) {
|
||||||
// Set up call and a temporary breakpoint after it.
|
ULONG bytesWritten = 0;
|
||||||
|
const HRESULT hr = ds->WriteVirtual(address, ptr, bytesToWrite, &bytesWritten);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = msgComFailed("WriteVirtual", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bytesToWrite -= bytesWritten;
|
||||||
|
ptr += bytesWritten;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr, QString *errorMessage)
|
||||||
|
{
|
||||||
|
*outDataPtr = 0;
|
||||||
|
CdbExceptionLoggerEventCallback exLogger(m_messagePrefix, m_access);
|
||||||
|
EventCallbackRedirector eventRedir(m_cif->debugClient, &exLogger);
|
||||||
|
// write input buffer
|
||||||
|
if (!inBuffer.isEmpty()) {
|
||||||
|
if (!writeToDebuggee(m_cif->debugDataSpaces, inBuffer, m_inBufferAddress, errorMessage))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, callCmd, errorMessage))
|
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, callCmd, errorMessage))
|
||||||
return false;
|
return false;
|
||||||
// Go and filter away break event
|
// Set up call and a temporary breakpoint after it.
|
||||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, QString(QLatin1Char('g')), errorMessage))
|
// Try to skip debuggee crash exceptions and dumper exceptions
|
||||||
|
// by using 'gh' (go handled)
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
const int oldExceptionCount = exLogger.exceptionCount();
|
||||||
|
// Go. If an exception occurs in loop 2, let the dumper handle it.
|
||||||
|
const QString goCmd = i ? QString(QLatin1String("gN")) : QString(QLatin1Char('g'));
|
||||||
|
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, goCmd, errorMessage))
|
||||||
return false;
|
return false;
|
||||||
HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS);
|
HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("WaitForEvent", hr);
|
*errorMessage = msgComFailed("WaitForEvent", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const int newExceptionCount = exLogger.exceptionCount();
|
||||||
|
// no new exceptions? -> break
|
||||||
|
if (oldExceptionCount == newExceptionCount)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (exLogger.exceptionCount()) {
|
||||||
|
const QString exMsgs = exLogger.exceptionMessages().join(QString(QLatin1Char(',')));
|
||||||
|
*errorMessage = QString::fromLatin1("Exceptions occurred during the dumper call: %1").arg(exMsgs);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// Read output
|
// Read output
|
||||||
hr = m_cif->debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
|
const HRESULT hr = m_cif->debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("ReadVirtual", hr);
|
*errorMessage = msgComFailed("ReadVirtual", hr);
|
||||||
return false;
|
return false;
|
||||||
@@ -421,7 +395,144 @@ bool CdbDumperHelper::callDumper(const DumperInputParameters &p, QByteArray *out
|
|||||||
*errorMessage = QString::fromLatin1("Dumper call '%1' failed ('%2').").arg(callCmd).arg(QLatin1Char(result));
|
*errorMessage = QString::fromLatin1("Dumper call '%1' failed ('%2').").arg(callCmd).arg(QLatin1Char(result));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*output = QByteArray(m_buffer + 1);
|
*outDataPtr = m_buffer + 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool dumpChildren, int source,
|
||||||
|
QList<WatchData> *result, QString *errorMessage)
|
||||||
|
{
|
||||||
|
// Check failure cache and supported types
|
||||||
|
if (m_failedTypes.contains(wd.type))
|
||||||
|
return DumpNotHandled;
|
||||||
|
const QtDumperHelper::TypeData td = m_helper.typeData(wd.type);
|
||||||
|
if (td.type == QtDumperHelper::UnknownType)
|
||||||
|
return DumpNotHandled;
|
||||||
|
|
||||||
|
// Now evaluate
|
||||||
|
const QString message = QCoreApplication::translate("CdbDumperHelper",
|
||||||
|
"Querying dumpers for '%1'/'%2' (%3)").
|
||||||
|
arg(wd.name, wd.exp, wd.type);
|
||||||
|
m_access->showDebuggerOutput(m_messagePrefix, message);
|
||||||
|
const DumpExecuteResult der = executeDump(wd, td, dumpChildren, source, result, errorMessage);
|
||||||
|
if (der == DumpExecuteOk)
|
||||||
|
return DumpOk;
|
||||||
|
// Cache types that fail due to complicated template size expressions.
|
||||||
|
// Exceptions OTOH might occur when accessing variables that are not
|
||||||
|
// yet initialized in a particular breakpoint. That should be ignored
|
||||||
|
if (der == DumpExecuteSizeFailed)
|
||||||
|
m_failedTypes.push_back(wd.type);
|
||||||
|
// log error
|
||||||
|
*errorMessage = QString::fromLatin1("Unable to dump '%1' (%2): %3").arg(wd.name, wd.type, *errorMessage);
|
||||||
|
m_access->showDebuggerOutput(m_messagePrefix, *errorMessage);
|
||||||
|
return DumpError;
|
||||||
|
}
|
||||||
|
|
||||||
|
CdbDumperHelper::DumpExecuteResult
|
||||||
|
CdbDumperHelper::executeDump(const WatchData &wd,
|
||||||
|
const QtDumperHelper::TypeData& td, bool dumpChildren, int source,
|
||||||
|
QList<WatchData> *result, QString *errorMessage)
|
||||||
|
{
|
||||||
|
QByteArray inBuffer;
|
||||||
|
QStringList extraParameters;
|
||||||
|
// Build parameter list.
|
||||||
|
m_helper.evaluationParameters(wd, td, QtDumperHelper::CdbDebugger, &inBuffer, &extraParameters);
|
||||||
|
// If the parameter list contains sizeof-expressions, execute them separately
|
||||||
|
// and replace them by the resulting numbers
|
||||||
|
const QString sizeOfExpr = QLatin1String("sizeof");
|
||||||
|
const QStringList::iterator eend = extraParameters.end();
|
||||||
|
for (QStringList::iterator it = extraParameters.begin() ; it != eend; ++it) {
|
||||||
|
// Strip 'sizeof(X)' to 'X' and query size
|
||||||
|
QString &ep = *it;
|
||||||
|
if (ep.startsWith(sizeOfExpr)) {
|
||||||
|
int size;
|
||||||
|
ep.truncate(ep.lastIndexOf(QLatin1Char(')')));
|
||||||
|
ep.remove(0, ep.indexOf(QLatin1Char('(')) + 1);
|
||||||
|
if (!getTypeSize(ep, &size, errorMessage))
|
||||||
|
return DumpExecuteSizeFailed;
|
||||||
|
if (loadDebug)
|
||||||
|
qDebug() << "Size" << size << ep;
|
||||||
|
ep = QString::number(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Execute call
|
||||||
|
QString callCmd;
|
||||||
|
QTextStream(&callCmd) << ".call " << m_dumpObjectSymbol
|
||||||
|
<< "(2,0," << wd.addr << ','
|
||||||
|
<< (dumpChildren ? 1 : 0) << ',' << extraParameters.join(QString(QLatin1Char(','))) << ')';
|
||||||
|
if (loadDebug)
|
||||||
|
qDebug() << "Query: " << wd.toString() << "\nwith: " << callCmd;
|
||||||
|
const char *outputData;
|
||||||
|
if (!callDumper(callCmd, inBuffer, &outputData, errorMessage))
|
||||||
|
return DumpExecuteCallFailed;
|
||||||
|
QtDumperResult dumpResult;
|
||||||
|
if (!QtDumperHelper::parseValue(outputData, &dumpResult)) {
|
||||||
|
*errorMessage = QLatin1String("Parsing of value query output failed.");
|
||||||
|
return DumpExecuteCallFailed;
|
||||||
|
}
|
||||||
|
*result = dumpResult.toWatchData(source);
|
||||||
|
return DumpExecuteOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simplify some types for sizeof expressions
|
||||||
|
static inline void simplifySizeExpression(QString *typeName)
|
||||||
|
{
|
||||||
|
typeName->replace(QLatin1String("std::basic_string<char,std::char_traits<char>,std::allocator<char>>"),
|
||||||
|
QLatin1String("std::string"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CdbDumperHelper::getTypeSize(const QString &typeNameIn, int *size, QString *errorMessage)
|
||||||
|
{
|
||||||
|
if (loadDebug > 1)
|
||||||
|
qDebug() << Q_FUNC_INFO << typeNameIn;
|
||||||
|
// Look up cache
|
||||||
|
const TypeSizeCache::const_iterator it = m_typeSizeCache.constFind(typeNameIn);
|
||||||
|
if (it != m_typeSizeCache.constEnd()) {
|
||||||
|
*size = it.value();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
QString typeName = typeNameIn;
|
||||||
|
simplifySizeExpression(&typeName);
|
||||||
|
// "std::" types sometimes only work without namespace.
|
||||||
|
// If it fails, try again with stripped namespace
|
||||||
|
*size = 0;
|
||||||
|
bool success = false;
|
||||||
|
do {
|
||||||
|
if (runTypeSizeQuery(typeName, size, errorMessage)) {
|
||||||
|
success = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const QString stdNameSpace = QLatin1String("std::");
|
||||||
|
if (!typeName.contains(stdNameSpace))
|
||||||
|
break;
|
||||||
|
typeName.remove(stdNameSpace);
|
||||||
|
errorMessage->clear();
|
||||||
|
if (!runTypeSizeQuery(typeName, size, errorMessage))
|
||||||
|
break;
|
||||||
|
success = true;
|
||||||
|
} while (false);
|
||||||
|
if (success)
|
||||||
|
m_typeSizeCache.insert(typeName, *size);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CdbDumperHelper::runTypeSizeQuery(const QString &typeName, int *size, QString *errorMessage)
|
||||||
|
{
|
||||||
|
// Retrieve by C++ expression. If we knew the module, we could make use
|
||||||
|
// of the TypeId query mechanism provided by the IDebugSymbolGroup.
|
||||||
|
DEBUG_VALUE sizeValue;
|
||||||
|
QString expression = QLatin1String("sizeof(");
|
||||||
|
expression += typeName;
|
||||||
|
expression += QLatin1Char(')');
|
||||||
|
if (!CdbDebugEnginePrivate::evaluateExpression(m_cif->debugControl,
|
||||||
|
expression, &sizeValue, errorMessage))
|
||||||
|
return false;
|
||||||
|
qint64 size64;
|
||||||
|
if (!CdbSymbolGroupContext::debugValueToInteger(sizeValue, &size64)) {
|
||||||
|
*errorMessage = QLatin1String("Expression result is not an integer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*size = static_cast<int>(size64);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,7 +30,10 @@
|
|||||||
#ifndef CDBDUMPERHELPER_H
|
#ifndef CDBDUMPERHELPER_H
|
||||||
#define CDBDUMPERHELPER_H
|
#define CDBDUMPERHELPER_H
|
||||||
|
|
||||||
|
#include "watchutils.h"
|
||||||
|
#include "cdbcom.h"
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QMap>
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
@@ -66,7 +69,8 @@ public:
|
|||||||
Failed
|
Failed
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit CdbDumperHelper(CdbComInterfaces *cif);
|
explicit CdbDumperHelper(IDebuggerManagerAccessForEngines *access,
|
||||||
|
CdbComInterfaces *cif);
|
||||||
~CdbDumperHelper();
|
~CdbDumperHelper();
|
||||||
|
|
||||||
State state() const { return m_state; }
|
State state() const { return m_state; }
|
||||||
@@ -76,31 +80,50 @@ public:
|
|||||||
void reset(const QString &library, bool enabled);
|
void reset(const QString &library, bool enabled);
|
||||||
|
|
||||||
// Call in a temporary breakpoint state to actually load.
|
// Call in a temporary breakpoint state to actually load.
|
||||||
void load(DebuggerManager *manager, IDebuggerManagerAccessForEngines *access);
|
void load(DebuggerManager *manager);
|
||||||
|
|
||||||
|
enum DumpResult { DumpNotHandled, DumpOk, DumpError };
|
||||||
|
DumpResult dumpType(const WatchData &d, bool dumpChildren, int source,
|
||||||
|
QList<WatchData> *result, QString *errorMessage);
|
||||||
|
|
||||||
|
// bool handlesType(const QString &typeName) const;
|
||||||
|
|
||||||
|
inline CdbComInterfaces *comInterfaces() const { return m_cif; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct DumperInputParameters;
|
|
||||||
|
|
||||||
void clearBuffer();
|
void clearBuffer();
|
||||||
bool resolveSymbols(QString *errorMessage);
|
bool resolveSymbols(QString *errorMessage);
|
||||||
bool getKnownTypes(QString *errorMessage);
|
bool getKnownTypes(QString *errorMessage);
|
||||||
bool callDumper(const DumperInputParameters &p, QByteArray *output, QString *errorMessage);
|
bool getTypeSize(const QString &typeName, int *size, QString *errorMessage);
|
||||||
inline QString statusMessage() const;
|
bool runTypeSizeQuery(const QString &typeName, int *size, QString *errorMessage);
|
||||||
|
bool callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr, QString *errorMessage);
|
||||||
|
|
||||||
|
enum DumpExecuteResult { DumpExecuteOk, DumpExecuteSizeFailed, DumpExecuteCallFailed };
|
||||||
|
DumpExecuteResult executeDump(const WatchData &wd,
|
||||||
|
const QtDumperHelper::TypeData& td, bool dumpChildren, int source,
|
||||||
|
QList<WatchData> *result, QString *errorMessage);
|
||||||
|
|
||||||
|
static bool writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &buffer, quint64 address, QString *errorMessage);
|
||||||
|
|
||||||
|
const QString m_messagePrefix;
|
||||||
State m_state;
|
State m_state;
|
||||||
|
IDebuggerManagerAccessForEngines *m_access;
|
||||||
CdbComInterfaces *m_cif;
|
CdbComInterfaces *m_cif;
|
||||||
|
|
||||||
QString m_library;
|
QString m_library;
|
||||||
QString m_dumpObjectSymbol;
|
QString m_dumpObjectSymbol;
|
||||||
QStringList m_knownTypes;
|
|
||||||
QString m_qtVersion;
|
|
||||||
QString m_qtNamespace;
|
|
||||||
|
|
||||||
quint64 m_inBufferAddress;
|
quint64 m_inBufferAddress;
|
||||||
unsigned long m_inBufferSize;
|
unsigned long m_inBufferSize;
|
||||||
quint64 m_outBufferAddress;
|
quint64 m_outBufferAddress;
|
||||||
unsigned long m_outBufferSize;
|
unsigned long m_outBufferSize;
|
||||||
char *m_buffer;
|
char *m_buffer;
|
||||||
|
|
||||||
|
typedef QMap<QString, int> TypeSizeCache;
|
||||||
|
TypeSizeCache m_typeSizeCache;
|
||||||
|
QStringList m_failedTypes;
|
||||||
|
|
||||||
|
QtDumperHelper m_helper;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
189
src/plugins/debugger/cdb/cdbstackframecontext.cpp
Normal file
189
src/plugins/debugger/cdb/cdbstackframecontext.cpp
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Qt Software Information (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at qt-sales@nokia.com.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "cdbstackframecontext.h"
|
||||||
|
#include "cdbsymbolgroupcontext.h"
|
||||||
|
#include "cdbdumperhelper.h"
|
||||||
|
#include "debuggeractions.h"
|
||||||
|
#include "watchhandler.h"
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
enum { debug = 0 };
|
||||||
|
|
||||||
|
namespace Debugger {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
enum { OwnerNewItem, OwnerSymbolGroup, OwnerDumper };
|
||||||
|
|
||||||
|
typedef QSharedPointer<CdbDumperHelper> SharedPointerCdbDumperHelper;
|
||||||
|
typedef QList<WatchData> WatchDataList;
|
||||||
|
|
||||||
|
// Put a sequence of WatchData into the model
|
||||||
|
class WatchHandlerModelInserter {
|
||||||
|
public:
|
||||||
|
explicit WatchHandlerModelInserter(WatchHandler *wh) : m_wh(wh) {}
|
||||||
|
|
||||||
|
inline WatchHandlerModelInserter & operator*() { return *this; }
|
||||||
|
inline WatchHandlerModelInserter &operator=(const WatchData &wd) {
|
||||||
|
m_wh->insertData(wd);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
inline WatchHandlerModelInserter &operator++() { return *this; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
WatchHandler *m_wh;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper to sort apart a sequence of WatchData using the Dumpers.
|
||||||
|
// Puts the stuff for which there is no dumper in the model
|
||||||
|
// as is and sets ownership to symbol group. The rest goes
|
||||||
|
// to the dumpers.
|
||||||
|
class WatchHandlerSorterInserter {
|
||||||
|
public:
|
||||||
|
explicit WatchHandlerSorterInserter(WatchHandler *wh,
|
||||||
|
const SharedPointerCdbDumperHelper &dumper);
|
||||||
|
|
||||||
|
inline WatchHandlerSorterInserter & operator*() { return *this; }
|
||||||
|
inline WatchHandlerSorterInserter &operator=(WatchData wd);
|
||||||
|
inline WatchHandlerSorterInserter &operator++() { return *this; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
WatchHandler *m_wh;
|
||||||
|
const SharedPointerCdbDumperHelper m_dumper;
|
||||||
|
QList<WatchData> m_dumperResult;
|
||||||
|
QStringList m_dumperINames;
|
||||||
|
};
|
||||||
|
|
||||||
|
WatchHandlerSorterInserter::WatchHandlerSorterInserter(WatchHandler *wh,
|
||||||
|
const SharedPointerCdbDumperHelper &dumper) :
|
||||||
|
m_wh(wh),
|
||||||
|
m_dumper(dumper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
WatchHandlerSorterInserter &WatchHandlerSorterInserter::operator=(WatchData wd)
|
||||||
|
{
|
||||||
|
// Is this a child belonging to some item handled by dumpers,
|
||||||
|
// such as d-elements of QStrings -> just ignore it.
|
||||||
|
if (const int dumperINamesCount = m_dumperINames.size()) {
|
||||||
|
for (int i = 0; i < dumperINamesCount; i++)
|
||||||
|
if (wd.iname.startsWith(m_dumperINames.at(i)))
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
QString errorMessage;
|
||||||
|
switch (m_dumper->dumpType(wd, true, OwnerDumper, &m_dumperResult, &errorMessage)) {
|
||||||
|
case CdbDumperHelper::DumpOk:
|
||||||
|
// Discard the original item and insert the dumper results
|
||||||
|
m_dumperINames.push_back(wd.iname + QLatin1Char('.'));
|
||||||
|
foreach(const WatchData &dwd, m_dumperResult)
|
||||||
|
m_wh->insertData(dwd);
|
||||||
|
break;
|
||||||
|
case CdbDumperHelper::DumpNotHandled:
|
||||||
|
case CdbDumperHelper::DumpError:
|
||||||
|
wd.source = OwnerSymbolGroup;
|
||||||
|
m_wh->insertData(wd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------CdbStackFrameContext
|
||||||
|
CdbStackFrameContext::CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||||
|
CdbSymbolGroupContext *symbolContext) :
|
||||||
|
m_useDumpers(dumper->state() == CdbDumperHelper::Loaded
|
||||||
|
&& theDebuggerBoolSetting(UseDebuggingHelpers)),
|
||||||
|
m_dumper(dumper),
|
||||||
|
m_symbolContext(symbolContext)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CdbStackFrameContext::assignValue(const QString &iname, const QString &value,
|
||||||
|
QString *newValue /* = 0 */, QString *errorMessage)
|
||||||
|
{
|
||||||
|
return m_symbolContext->assignValue(iname, value, newValue, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CdbStackFrameContext::populateModelInitially(WatchHandler *wh, QString *errorMessage)
|
||||||
|
{
|
||||||
|
if (debug)
|
||||||
|
qDebug() << "populateModelInitially";
|
||||||
|
const bool rc = m_useDumpers ?
|
||||||
|
CdbSymbolGroupContext::populateModelInitially(m_symbolContext,
|
||||||
|
WatchHandlerSorterInserter(wh, m_dumper),
|
||||||
|
errorMessage) :
|
||||||
|
CdbSymbolGroupContext::populateModelInitially(m_symbolContext,
|
||||||
|
WatchHandlerModelInserter(wh),
|
||||||
|
errorMessage);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CdbStackFrameContext::completeModel(const QList<WatchData> &incompleteLocals,
|
||||||
|
WatchHandler *wh,
|
||||||
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
if (debug) {
|
||||||
|
QDebug nsp = qDebug().nospace();
|
||||||
|
nsp << ">completeModel ";
|
||||||
|
foreach (const WatchData &wd, incompleteLocals)
|
||||||
|
nsp << wd.iname << ' ';
|
||||||
|
nsp << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_useDumpers) {
|
||||||
|
return CdbSymbolGroupContext::completeModel(m_symbolContext, incompleteLocals,
|
||||||
|
WatchHandlerModelInserter(wh),
|
||||||
|
errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand dumper items
|
||||||
|
int handledDumpers = 0;
|
||||||
|
foreach (const WatchData &cwd, incompleteLocals) {
|
||||||
|
if (cwd.source == OwnerDumper) { // You already know that.
|
||||||
|
WatchData wd = cwd;
|
||||||
|
wd.setAllUnneeded();
|
||||||
|
wh->insertData(wd);
|
||||||
|
handledDumpers++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (handledDumpers == incompleteLocals.size())
|
||||||
|
return true;
|
||||||
|
// Expand symbol group items
|
||||||
|
return CdbSymbolGroupContext::completeModel(m_symbolContext, incompleteLocals,
|
||||||
|
WatchHandlerSorterInserter(wh, m_dumper),
|
||||||
|
errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
CdbStackFrameContext::~CdbStackFrameContext()
|
||||||
|
{
|
||||||
|
delete m_symbolContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Debugger
|
74
src/plugins/debugger/cdb/cdbstackframecontext.h
Normal file
74
src/plugins/debugger/cdb/cdbstackframecontext.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Qt Software Information (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at qt-sales@nokia.com.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CDBSTACKFRAMECONTEXT_H
|
||||||
|
#define CDBSTACKFRAMECONTEXT_H
|
||||||
|
|
||||||
|
#include <QtCore/QList>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
|
||||||
|
namespace Debugger {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class WatchData;
|
||||||
|
class WatchHandler;
|
||||||
|
class CdbSymbolGroupContext;
|
||||||
|
class CdbDumperHelper;
|
||||||
|
|
||||||
|
/* CdbStackFrameContext manages a symbol group context and
|
||||||
|
* a dumper context. It dispatches calls between the local items
|
||||||
|
* that are handled by the symbol group and those that are handled by the dumpers. */
|
||||||
|
|
||||||
|
class CdbStackFrameContext
|
||||||
|
{
|
||||||
|
Q_DISABLE_COPY(CdbStackFrameContext)
|
||||||
|
public:
|
||||||
|
explicit CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||||
|
CdbSymbolGroupContext *symbolContext);
|
||||||
|
~CdbStackFrameContext();
|
||||||
|
|
||||||
|
bool assignValue(const QString &iname, const QString &value,
|
||||||
|
QString *newValue /* = 0 */, QString *errorMessage);
|
||||||
|
|
||||||
|
bool populateModelInitially(WatchHandler *wh, QString *errorMessage);
|
||||||
|
|
||||||
|
bool completeModel(const QList<WatchData> &incompleteLocals,
|
||||||
|
WatchHandler *wh,
|
||||||
|
QString *errorMessage);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const bool m_useDumpers;
|
||||||
|
const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||||
|
CdbSymbolGroupContext *m_symbolContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Debugger
|
||||||
|
|
||||||
|
#endif // CDBSTACKFRAMECONTEXT_H
|
@@ -28,9 +28,11 @@
|
|||||||
**************************************************************************/
|
**************************************************************************/
|
||||||
|
|
||||||
#include "cdbstacktracecontext.h"
|
#include "cdbstacktracecontext.h"
|
||||||
|
#include "cdbstackframecontext.h"
|
||||||
#include "cdbbreakpoint.h"
|
#include "cdbbreakpoint.h"
|
||||||
#include "cdbsymbolgroupcontext.h"
|
#include "cdbsymbolgroupcontext.h"
|
||||||
#include "cdbdebugengine_p.h"
|
#include "cdbdebugengine_p.h"
|
||||||
|
#include "cdbdumperhelper.h"
|
||||||
|
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
#include <QtCore/QTextStream>
|
#include <QtCore/QTextStream>
|
||||||
@@ -38,18 +40,20 @@
|
|||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
CdbStackTraceContext::CdbStackTraceContext(CdbComInterfaces *cif) :
|
CdbStackTraceContext::CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper) :
|
||||||
m_cif(cif),
|
m_dumper(dumper),
|
||||||
|
m_cif(dumper->comInterfaces()),
|
||||||
m_instructionOffset(0)
|
m_instructionOffset(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CdbStackTraceContext *CdbStackTraceContext::create(CdbComInterfaces *cif,
|
CdbStackTraceContext *CdbStackTraceContext::create(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||||
unsigned long threadId,
|
unsigned long threadId,
|
||||||
QString *errorMessage)
|
QString *errorMessage)
|
||||||
{
|
{
|
||||||
if (debugCDB)
|
if (debugCDB)
|
||||||
qDebug() << Q_FUNC_INFO << threadId;
|
qDebug() << Q_FUNC_INFO << threadId;
|
||||||
|
CdbComInterfaces *cif = dumper->comInterfaces();
|
||||||
HRESULT hr = cif->debugSystemObjects->SetCurrentThreadId(threadId);
|
HRESULT hr = cif->debugSystemObjects->SetCurrentThreadId(threadId);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = QString::fromLatin1("%1: SetCurrentThreadId %2 failed: %3").
|
*errorMessage = QString::fromLatin1("%1: SetCurrentThreadId %2 failed: %3").
|
||||||
@@ -60,7 +64,7 @@ CdbStackTraceContext *CdbStackTraceContext::create(CdbComInterfaces *cif,
|
|||||||
}
|
}
|
||||||
// fill the DEBUG_STACK_FRAME array
|
// fill the DEBUG_STACK_FRAME array
|
||||||
ULONG frameCount;
|
ULONG frameCount;
|
||||||
CdbStackTraceContext *ctx = new CdbStackTraceContext(cif);
|
CdbStackTraceContext *ctx = new CdbStackTraceContext(dumper);
|
||||||
hr = cif->debugControl->GetStackTrace(0, 0, 0, ctx->m_cdbFrames, CdbStackTraceContext::maxFrames, &frameCount);
|
hr = cif->debugControl->GetStackTrace(0, 0, 0, ctx->m_cdbFrames, CdbStackTraceContext::maxFrames, &frameCount);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
delete ctx;
|
delete ctx;
|
||||||
@@ -77,7 +81,7 @@ CdbStackTraceContext *CdbStackTraceContext::create(CdbComInterfaces *cif,
|
|||||||
|
|
||||||
CdbStackTraceContext::~CdbStackTraceContext()
|
CdbStackTraceContext::~CdbStackTraceContext()
|
||||||
{
|
{
|
||||||
qDeleteAll(m_symbolContexts);
|
qDeleteAll(m_frameContexts);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessage*/)
|
bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessage*/)
|
||||||
@@ -85,8 +89,8 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa
|
|||||||
if (debugCDB)
|
if (debugCDB)
|
||||||
qDebug() << Q_FUNC_INFO << frameCount;
|
qDebug() << Q_FUNC_INFO << frameCount;
|
||||||
|
|
||||||
m_symbolContexts.resize(frameCount);
|
m_frameContexts.resize(frameCount);
|
||||||
qFill(m_symbolContexts, static_cast<CdbSymbolGroupContext*>(0));
|
qFill(m_frameContexts, static_cast<CdbStackFrameContext*>(0));
|
||||||
|
|
||||||
// Convert the DEBUG_STACK_FRAMEs to our StackFrame structure and populate the frames
|
// Convert the DEBUG_STACK_FRAMEs to our StackFrame structure and populate the frames
|
||||||
WCHAR wszBuf[MAX_PATH];
|
WCHAR wszBuf[MAX_PATH];
|
||||||
@@ -114,28 +118,28 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CdbSymbolGroupContext *CdbStackTraceContext::symbolGroupContextAt(int index, QString *errorMessage)
|
CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *errorMessage)
|
||||||
{
|
{
|
||||||
// Create a symbol group on demand
|
// Create a frame on demand
|
||||||
if (debugCDB)
|
if (debugCDB)
|
||||||
qDebug() << Q_FUNC_INFO << index;
|
qDebug() << Q_FUNC_INFO << index;
|
||||||
|
|
||||||
if (index < 0 || index >= m_symbolContexts.size()) {
|
if (index < 0 || index >= m_frameContexts.size()) {
|
||||||
*errorMessage = QString::fromLatin1("%1: Index %2 out of range %3.").
|
*errorMessage = QString::fromLatin1("%1: Index %2 out of range %3.").
|
||||||
arg(QLatin1String(Q_FUNC_INFO)).arg(index).arg(m_symbolContexts.size());
|
arg(QLatin1String(Q_FUNC_INFO)).arg(index).arg(m_frameContexts.size());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (m_frameContexts.at(index))
|
||||||
if (m_symbolContexts.at(index))
|
return m_frameContexts.at(index);
|
||||||
return m_symbolContexts.at(index);
|
|
||||||
CIDebugSymbolGroup *sg = createSymbolGroup(index, errorMessage);
|
CIDebugSymbolGroup *sg = createSymbolGroup(index, errorMessage);
|
||||||
if (!sg)
|
if (!sg)
|
||||||
return 0;
|
return 0;
|
||||||
CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(QLatin1String("local"), sg, errorMessage);
|
CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(QLatin1String("local"), sg, errorMessage);
|
||||||
if (!sc)
|
if (!sc)
|
||||||
return 0; \
|
return 0;
|
||||||
m_symbolContexts[index] = sc;
|
CdbStackFrameContext *fr = new CdbStackFrameContext(m_dumper, sc);
|
||||||
return sc;
|
m_frameContexts[index] = fr;
|
||||||
|
return fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *errorMessage)
|
CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *errorMessage)
|
||||||
|
@@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtCore/QVector>
|
#include <QtCore/QVector>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QTextStream;
|
class QTextStream;
|
||||||
@@ -46,21 +47,23 @@ namespace Internal {
|
|||||||
|
|
||||||
struct CdbComInterfaces;
|
struct CdbComInterfaces;
|
||||||
class CdbSymbolGroupContext;
|
class CdbSymbolGroupContext;
|
||||||
|
class CdbStackFrameContext;
|
||||||
|
class CdbDumperHelper;
|
||||||
|
|
||||||
/* Context representing a break point stack consisting of several frames.
|
/* Context representing a break point stack consisting of several frames.
|
||||||
* Maintains an on-demand constructed list of CdbSymbolGroupContext
|
* Maintains an on-demand constructed list of CdbStackFrameContext
|
||||||
* containining the local variables of the stack. */
|
* containining the local variables of the stack. */
|
||||||
|
|
||||||
class CdbStackTraceContext
|
class CdbStackTraceContext
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY(CdbStackTraceContext)
|
Q_DISABLE_COPY(CdbStackTraceContext)
|
||||||
|
|
||||||
explicit CdbStackTraceContext(CdbComInterfaces *cif);
|
explicit CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper);
|
||||||
public:
|
public:
|
||||||
enum { maxFrames = 100 };
|
enum { maxFrames = 100 };
|
||||||
|
|
||||||
~CdbStackTraceContext();
|
~CdbStackTraceContext();
|
||||||
static CdbStackTraceContext *create(CdbComInterfaces *cif,
|
static CdbStackTraceContext *create(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||||
unsigned long threadid,
|
unsigned long threadid,
|
||||||
QString *errorMessage);
|
QString *errorMessage);
|
||||||
|
|
||||||
@@ -70,7 +73,7 @@ public:
|
|||||||
// Top-Level instruction offset for disassembler
|
// Top-Level instruction offset for disassembler
|
||||||
ULONG64 instructionOffset() const { return m_instructionOffset; }
|
ULONG64 instructionOffset() const { return m_instructionOffset; }
|
||||||
|
|
||||||
CdbSymbolGroupContext *symbolGroupContextAt(int index, QString *errorMessage);
|
CdbStackFrameContext*frameContextAt(int index, QString *errorMessage);
|
||||||
|
|
||||||
// Format for logging
|
// Format for logging
|
||||||
void format(QTextStream &str) const;
|
void format(QTextStream &str) const;
|
||||||
@@ -80,10 +83,11 @@ private:
|
|||||||
bool init(unsigned long frameCount, QString *errorMessage);
|
bool init(unsigned long frameCount, QString *errorMessage);
|
||||||
CIDebugSymbolGroup *createSymbolGroup(int index, QString *errorMessage);
|
CIDebugSymbolGroup *createSymbolGroup(int index, QString *errorMessage);
|
||||||
|
|
||||||
|
const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||||
CdbComInterfaces *m_cif;
|
CdbComInterfaces *m_cif;
|
||||||
|
|
||||||
DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
|
DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
|
||||||
QVector <CdbSymbolGroupContext*> m_symbolContexts;
|
QVector <CdbStackFrameContext*> m_frameContexts;
|
||||||
QList<StackFrame> m_frames;
|
QList<StackFrame> m_frames;
|
||||||
ULONG64 m_instructionOffset;
|
ULONG64 m_instructionOffset;
|
||||||
};
|
};
|
||||||
|
@@ -200,16 +200,6 @@ QString CdbSymbolGroupContext::toString(bool verbose) const
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CdbSymbolGroupContext::isSymbolDisplayable(const DEBUG_SYMBOL_PARAMETERS &p)
|
|
||||||
{
|
|
||||||
if (p.Flags & (DEBUG_SYMBOL_IS_LOCAL|DEBUG_SYMBOL_IS_ARGUMENT))
|
|
||||||
return true;
|
|
||||||
// Do not display static members.
|
|
||||||
if (p.Flags & DEBUG_SYMBOL_READ_ONLY)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
CdbSymbolGroupContext::SymbolState CdbSymbolGroupContext::symbolState(unsigned long index) const
|
CdbSymbolGroupContext::SymbolState CdbSymbolGroupContext::symbolState(unsigned long index) const
|
||||||
{
|
{
|
||||||
return getSymbolState(m_symbolParameters.at(index));
|
return getSymbolState(m_symbolParameters.at(index));
|
||||||
@@ -499,144 +489,26 @@ QString CdbSymbolGroupContext::debugValueToString(const DEBUG_VALUE &dv, CIDebug
|
|||||||
return formatArrayHelper(dv.RawBytes, 24, integerBase);
|
return formatArrayHelper(dv.RawBytes, 24, integerBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - Watch model functions
|
bool CdbSymbolGroupContext::debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value)
|
||||||
class WatchDataBackInserter {
|
|
||||||
public:
|
|
||||||
explicit WatchDataBackInserter(QList<WatchData> &wh) : m_wh(wh) {}
|
|
||||||
|
|
||||||
WatchDataBackInserter & operator*() { return *this; }
|
|
||||||
WatchDataBackInserter &operator=(const WatchData &wd) {
|
|
||||||
m_wh.push_back(wd);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
WatchDataBackInserter &operator++() { return *this; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
QList<WatchData> &m_wh;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool insertChildrenRecursion(const QString &iname,
|
|
||||||
CdbSymbolGroupContext *sg,
|
|
||||||
WatchHandler *watchHandler,
|
|
||||||
int maxRecursionLevel,
|
|
||||||
int level,
|
|
||||||
QString *errorMessage,
|
|
||||||
int *childCount = 0);
|
|
||||||
|
|
||||||
// Insert a symbol (and its first level children depending on forceRecursion)
|
|
||||||
static bool insertSymbolRecursion(WatchData wd,
|
|
||||||
CdbSymbolGroupContext *sg,
|
|
||||||
WatchHandler *watchHandler,
|
|
||||||
int maxRecursionLevel,
|
|
||||||
int level,
|
|
||||||
QString *errorMessage)
|
|
||||||
{
|
{
|
||||||
// Find out whether to recurse (has children or at least knows it has children)
|
*value = 0;
|
||||||
// Open next level if specified by recursion depth or child is already expanded
|
switch (dv.Type) {
|
||||||
// (Sometimes, some root children are already expanded after creating the context).
|
case DEBUG_VALUE_INT8:
|
||||||
const bool hasChildren = wd.childCount > 0 || wd.isChildrenNeeded();
|
*value = dv.I8;
|
||||||
const bool recurse = hasChildren && (level < maxRecursionLevel || sg->isExpanded(wd.iname));
|
|
||||||
if (debug)
|
|
||||||
qDebug() << Q_FUNC_INFO << '\n' << wd.iname << "level=" << level << "recurse=" << recurse;
|
|
||||||
bool rc = true;
|
|
||||||
if (recurse) { // Determine number of children and indicate in model
|
|
||||||
int childCount;
|
|
||||||
rc = insertChildrenRecursion(wd.iname, sg, watchHandler, maxRecursionLevel, level, errorMessage, &childCount);
|
|
||||||
if (rc) {
|
|
||||||
wd.setChildCount(childCount);
|
|
||||||
wd.setChildrenUnneeded();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No further recursion at this level, pretend entry is complete
|
|
||||||
if (wd.isChildrenNeeded()) {
|
|
||||||
wd.setChildCount(1);
|
|
||||||
wd.setChildrenUnneeded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (debug)
|
|
||||||
qDebug() << " INSERTING: at " << level << wd.toString();
|
|
||||||
watchHandler->insertData(wd);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert the children of prefix.
|
|
||||||
static bool insertChildrenRecursion(const QString &iname,
|
|
||||||
CdbSymbolGroupContext *sg,
|
|
||||||
WatchHandler *watchHandler,
|
|
||||||
int maxRecursionLevel,
|
|
||||||
int level,
|
|
||||||
QString *errorMessage,
|
|
||||||
int *childCountPtr)
|
|
||||||
{
|
|
||||||
if (debug > 1)
|
|
||||||
qDebug() << Q_FUNC_INFO << '\n' << iname << level;
|
|
||||||
|
|
||||||
QList<WatchData> watchList;
|
|
||||||
// This implicitly enforces expansion
|
|
||||||
if (!sg->getChildSymbols(iname, WatchDataBackInserter(watchList), errorMessage))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const int childCount = watchList.size();
|
|
||||||
if (childCountPtr)
|
|
||||||
*childCountPtr = childCount;
|
|
||||||
int succeededChildCount = 0;
|
|
||||||
for (int c = 0; c < childCount; c++) {
|
|
||||||
const WatchData &wd = watchList.at(c);
|
|
||||||
if (wd.isValid()) { // We sometimes get empty names for deeply nested data
|
|
||||||
if (!insertSymbolRecursion(wd, sg, watchHandler, maxRecursionLevel, level + 1, errorMessage))
|
|
||||||
return false;
|
|
||||||
succeededChildCount++;
|
|
||||||
} else {
|
|
||||||
const QString msg = QString::fromLatin1("WARNING: Skipping invalid child symbol #%2 (type %3) of '%4'.").
|
|
||||||
arg(QLatin1String(Q_FUNC_INFO)).arg(c).arg(wd.type, iname);
|
|
||||||
qWarning("%s\n", qPrintable(msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (childCountPtr)
|
|
||||||
*childCountPtr = succeededChildCount;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
case DEBUG_VALUE_INT16:
|
||||||
|
*value = static_cast<short>(dv.I16);
|
||||||
bool CdbSymbolGroupContext::populateModelInitially(CdbSymbolGroupContext *sg,
|
|
||||||
WatchHandler *watchHandler,
|
|
||||||
QString *errorMessage)
|
|
||||||
{
|
|
||||||
if (debugCDB)
|
|
||||||
qDebug() << "###" << Q_FUNC_INFO;
|
|
||||||
|
|
||||||
// Insert root items
|
|
||||||
QList<WatchData> watchList;
|
|
||||||
if (!sg->getChildSymbols(sg->prefix(), WatchDataBackInserter(watchList), errorMessage))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
foreach(const WatchData &wd, watchList)
|
|
||||||
if (!insertSymbolRecursion(wd, sg, watchHandler, 0, 0, errorMessage))
|
|
||||||
return false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
case DEBUG_VALUE_INT32:
|
||||||
|
*value = static_cast<long>(dv.I32);
|
||||||
bool CdbSymbolGroupContext::completeModel(CdbSymbolGroupContext *sg,
|
|
||||||
const QList<WatchData> &incompleteLocals,
|
|
||||||
WatchHandler *watchHandler,
|
|
||||||
QString *errorMessage)
|
|
||||||
{
|
|
||||||
if (debugCDB)
|
|
||||||
qDebug().nospace() << "###>" << Q_FUNC_INFO << ' ' << incompleteLocals.size() << '\n';
|
|
||||||
// The view reinserts any node being expanded with flag 'ChildrenNeeded'.
|
|
||||||
// Recurse down one level in context unless this is already the case.
|
|
||||||
foreach(WatchData wd, incompleteLocals) {
|
|
||||||
const bool contextExpanded = sg->isExpanded(wd.iname);
|
|
||||||
if (debug)
|
|
||||||
qDebug() << " " << wd.iname << "CE=" << contextExpanded;
|
|
||||||
if (contextExpanded) { // You know that already.
|
|
||||||
wd.setChildrenUnneeded();
|
|
||||||
watchHandler->insertData(wd);
|
|
||||||
} else {
|
|
||||||
if (!insertSymbolRecursion(wd, sg, watchHandler, 1, 0, errorMessage))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
|
case DEBUG_VALUE_INT64:
|
||||||
|
*value = static_cast<long long>(dv.I64);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
#define CDBSYMBOLGROUPCONTEXT_H
|
#define CDBSYMBOLGROUPCONTEXT_H
|
||||||
|
|
||||||
#include "cdbcom.h"
|
#include "cdbcom.h"
|
||||||
|
#include "watchhandler.h"
|
||||||
|
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtCore/QVector>
|
#include <QtCore/QVector>
|
||||||
@@ -72,11 +73,13 @@ public:
|
|||||||
bool assignValue(const QString &iname, const QString &value,
|
bool assignValue(const QString &iname, const QString &value,
|
||||||
QString *newValue /* = 0 */, QString *errorMessage);
|
QString *newValue /* = 0 */, QString *errorMessage);
|
||||||
|
|
||||||
static bool populateModelInitially(CdbSymbolGroupContext *sg, WatchHandler *wh, QString *errorMessage);
|
template <class OutputIterator>
|
||||||
|
static bool populateModelInitially(CdbSymbolGroupContext *sg, OutputIterator it, QString *errorMessage);
|
||||||
|
|
||||||
|
template <class OutputIterator>
|
||||||
static bool completeModel(CdbSymbolGroupContext *sg,
|
static bool completeModel(CdbSymbolGroupContext *sg,
|
||||||
const QList<WatchData> &incompleteLocals,
|
const QList<WatchData> &incompleteLocals,
|
||||||
WatchHandler *wh,
|
OutputIterator it,
|
||||||
QString *errorMessage);
|
QString *errorMessage);
|
||||||
|
|
||||||
// Retrieve child symbols of prefix as a sequence of WatchData.
|
// Retrieve child symbols of prefix as a sequence of WatchData.
|
||||||
@@ -92,6 +95,7 @@ public:
|
|||||||
|
|
||||||
// Helper to convert a DEBUG_VALUE structure to a string representation
|
// Helper to convert a DEBUG_VALUE structure to a string representation
|
||||||
static QString debugValueToString(const DEBUG_VALUE &dv, CIDebugControl *ctl, QString *type = 0, int integerBase = 10);
|
static QString debugValueToString(const DEBUG_VALUE &dv, CIDebugControl *ctl, QString *type = 0, int integerBase = 10);
|
||||||
|
static bool debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value);
|
||||||
|
|
||||||
// format an array of unsigned longs as "0x323, 0x2322, ..."
|
// format an array of unsigned longs as "0x323, 0x2322, ..."
|
||||||
static QString hexFormatArray(const unsigned short *array, int size);
|
static QString hexFormatArray(const unsigned short *array, int size);
|
||||||
@@ -125,26 +129,25 @@ private:
|
|||||||
QVector<DEBUG_SYMBOL_PARAMETERS> m_symbolParameters;
|
QVector<DEBUG_SYMBOL_PARAMETERS> m_symbolParameters;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class OutputIterator>
|
// Helper to a sequence of WatchData into a list.
|
||||||
bool CdbSymbolGroupContext::getChildSymbols(const QString &prefix, OutputIterator it, QString *errorMessage)
|
class WatchDataBackInserter {
|
||||||
{
|
public:
|
||||||
unsigned long start;
|
explicit WatchDataBackInserter(QList<WatchData> &wh) : m_wh(wh) {}
|
||||||
unsigned long parentId;
|
|
||||||
if (!getChildSymbolsPosition(prefix, &start, &parentId, errorMessage))
|
inline WatchDataBackInserter & operator*() { return *this; }
|
||||||
return false;
|
inline WatchDataBackInserter &operator=(const WatchData &wd) {
|
||||||
// Skip over expanded children
|
m_wh.push_back(wd);
|
||||||
const unsigned long end = m_symbolParameters.size();
|
return *this;
|
||||||
for (unsigned long s = start; s < end; ++s) {
|
|
||||||
const DEBUG_SYMBOL_PARAMETERS &p = m_symbolParameters.at(s);
|
|
||||||
if (p.ParentSymbol == parentId && isSymbolDisplayable(p)) {
|
|
||||||
*it = symbolAt(s);
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
}
|
inline WatchDataBackInserter &operator++() { return *this; }
|
||||||
return true;
|
|
||||||
}
|
private:
|
||||||
|
QList<WatchData> &m_wh;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|
||||||
|
#include "cdbsymbolgroupcontext_tpl.h"
|
||||||
|
|
||||||
#endif // CDBSYMBOLGROUPCONTEXT_H
|
#endif // CDBSYMBOLGROUPCONTEXT_H
|
||||||
|
@@ -146,6 +146,7 @@ private:
|
|||||||
friend class ScriptEngine;
|
friend class ScriptEngine;
|
||||||
friend struct CdbDebugEnginePrivate;
|
friend struct CdbDebugEnginePrivate;
|
||||||
friend class CdbDumperHelper;
|
friend class CdbDumperHelper;
|
||||||
|
friend class CdbExceptionLoggerEventCallback;
|
||||||
|
|
||||||
// called from the engines after successful startup
|
// called from the engines after successful startup
|
||||||
virtual void notifyInferiorStopRequested() = 0;
|
virtual void notifyInferiorStopRequested() = 0;
|
||||||
|
@@ -55,6 +55,7 @@
|
|||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
#include <QtCore/QTime>
|
#include <QtCore/QTime>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
#include <QtCore/QTextStream>
|
||||||
|
|
||||||
#include <QtGui/QAction>
|
#include <QtGui/QAction>
|
||||||
#include <QtGui/QApplication>
|
#include <QtGui/QApplication>
|
||||||
@@ -2913,44 +2914,6 @@ void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
|
|||||||
|
|
||||||
static const QString strNotInScope = QLatin1String("<not in scope>");
|
static const QString strNotInScope = QLatin1String("<not in scope>");
|
||||||
|
|
||||||
static QString quoteUnprintableLatin1(const QByteArray &ba)
|
|
||||||
{
|
|
||||||
QString res;
|
|
||||||
char buf[10];
|
|
||||||
for (int i = 0, n = ba.size(); i != n; ++i) {
|
|
||||||
unsigned char c = ba.at(i);
|
|
||||||
if (isprint(c)) {
|
|
||||||
res += c;
|
|
||||||
} else {
|
|
||||||
qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
|
|
||||||
res += buf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString decodeData(QByteArray ba, int encoding)
|
|
||||||
{
|
|
||||||
switch (encoding) {
|
|
||||||
case 0: // unencoded 8 bit data
|
|
||||||
return quoteUnprintableLatin1(ba);
|
|
||||||
case 1: // base64 encoded 8 bit data, used for QByteArray
|
|
||||||
ba = QByteArray::fromBase64(ba);
|
|
||||||
return '"' + quoteUnprintableLatin1(ba) + '"';
|
|
||||||
case 2: // base64 encoded 16 bit data, used for QString
|
|
||||||
ba = QByteArray::fromBase64(ba);
|
|
||||||
return '"' + QString::fromUtf16((ushort *)ba.data(), ba.size() / 2) + '"';
|
|
||||||
case 3: // base64 encoded 32 bit data
|
|
||||||
ba = QByteArray::fromBase64(ba);
|
|
||||||
return '"' + QString::fromUcs4((uint *)ba.data(), ba.size() / 4) + '"';
|
|
||||||
break;
|
|
||||||
case 4: // base64 encoded 16 bit data, without quotes (see 2)
|
|
||||||
ba = QByteArray::fromBase64(ba);
|
|
||||||
return QString::fromUtf16((ushort *)ba.data(), ba.size() / 2);
|
|
||||||
}
|
|
||||||
return "<Encoding error>";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setWatchDataValue(WatchData &data, const GdbMi &mi,
|
static void setWatchDataValue(WatchData &data, const GdbMi &mi,
|
||||||
int encoding = 0)
|
int encoding = 0)
|
||||||
{
|
{
|
||||||
@@ -3041,15 +3004,7 @@ bool GdbEngine::hasDebuggingHelperForType(const QString &type) const
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// simple types
|
// simple types
|
||||||
if (m_availableSimpleDebuggingHelpers.contains(type))
|
return m_dumperHelper.type(type) != QtDumperHelper::UnknownType;
|
||||||
return true;
|
|
||||||
|
|
||||||
// templates
|
|
||||||
QString tmplate;
|
|
||||||
QString inner;
|
|
||||||
if (!extractTemplate(type, &tmplate, &inner))
|
|
||||||
return false;
|
|
||||||
return m_availableSimpleDebuggingHelpers.contains(tmplate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::runDirectDebuggingHelper(const WatchData &data, bool dumpChildren)
|
void GdbEngine::runDirectDebuggingHelper(const WatchData &data, bool dumpChildren)
|
||||||
@@ -3080,145 +3035,33 @@ void GdbEngine::runDebuggingHelper(const WatchData &data0, bool dumpChildren)
|
|||||||
}
|
}
|
||||||
WatchData data = data0;
|
WatchData data = data0;
|
||||||
QTC_ASSERT(!data.exp.isEmpty(), return);
|
QTC_ASSERT(!data.exp.isEmpty(), return);
|
||||||
QString tmplate;
|
|
||||||
QString inner;
|
|
||||||
bool isTemplate = extractTemplate(data.type, &tmplate, &inner);
|
|
||||||
QStringList inners = inner.split('@');
|
|
||||||
if (inners.at(0).isEmpty())
|
|
||||||
inners.clear();
|
|
||||||
for (int i = 0; i != inners.size(); ++i)
|
|
||||||
inners[i] = inners[i].simplified();
|
|
||||||
|
|
||||||
QString outertype = isTemplate ? tmplate : data.type;
|
QByteArray params;
|
||||||
// adjust the data extract
|
QStringList extraArgs;
|
||||||
if (outertype == m_namespace + "QWidget")
|
const QtDumperHelper::TypeData td = m_dumperHelper.typeData(data0.type);
|
||||||
outertype = m_namespace + "QObject";
|
m_dumperHelper.evaluationParameters(data, td, QtDumperHelper::GdbDebugger, ¶ms, &extraArgs);
|
||||||
|
|
||||||
QString extraArgs[4];
|
|
||||||
extraArgs[0] = "0";
|
|
||||||
extraArgs[1] = "0";
|
|
||||||
extraArgs[2] = "0";
|
|
||||||
extraArgs[3] = "0";
|
|
||||||
|
|
||||||
int extraArgCount = 0;
|
|
||||||
|
|
||||||
// "generic" template dumpers: passing sizeof(argument)
|
|
||||||
// gives already most information the dumpers need
|
|
||||||
foreach (const QString &arg, inners)
|
|
||||||
extraArgs[extraArgCount++] = sizeofTypeExpression(arg);
|
|
||||||
|
|
||||||
// in rare cases we need more or less:
|
|
||||||
if (outertype == m_namespace + "QObject") {
|
|
||||||
extraArgs[0] = "(char*)&((('"
|
|
||||||
+ m_namespace + "QObjectPrivate'*)&"
|
|
||||||
+ data.exp + ")->children)-(char*)&" + data.exp;
|
|
||||||
} else if (outertype == m_namespace + "QVector") {
|
|
||||||
extraArgs[1] = "(char*)&(("
|
|
||||||
+ data.exp + ").d->array)-(char*)" + data.exp + ".d";
|
|
||||||
} else if (outertype == m_namespace + "QObjectSlot"
|
|
||||||
|| outertype == m_namespace + "QObjectSignal") {
|
|
||||||
// we need the number out of something like
|
|
||||||
// iname="local.ob.slots.2" // ".deleteLater()"?
|
|
||||||
int pos = data.iname.lastIndexOf('.');
|
|
||||||
QString slotNumber = data.iname.mid(pos + 1);
|
|
||||||
QTC_ASSERT(slotNumber.toInt() != -1, /**/);
|
|
||||||
extraArgs[0] = slotNumber;
|
|
||||||
} else if (outertype == m_namespace + "QMap" || outertype == m_namespace + "QMultiMap") {
|
|
||||||
QString nodetype;
|
|
||||||
if (m_qtVersion >= (4 << 16) + (5 << 8) + 0) {
|
|
||||||
nodetype = m_namespace + "QMapNode";
|
|
||||||
nodetype += data.type.mid(outertype.size());
|
|
||||||
} else {
|
|
||||||
// FIXME: doesn't work for QMultiMap
|
|
||||||
nodetype = data.type + "::Node";
|
|
||||||
}
|
|
||||||
//qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
|
|
||||||
// << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
|
|
||||||
extraArgs[2] = sizeofTypeExpression(nodetype);
|
|
||||||
extraArgs[3] = "(size_t)&(('" + nodetype + "'*)0)->value";
|
|
||||||
} else if (outertype == m_namespace + "QMapNode") {
|
|
||||||
extraArgs[2] = sizeofTypeExpression(data.type);
|
|
||||||
extraArgs[3] = "(size_t)&(('" + data.type + "'*)0)->value";
|
|
||||||
} else if (outertype == "std::vector" || outertype == "vector") {
|
|
||||||
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
|
|
||||||
if (inners.at(0) == "bool") {
|
|
||||||
outertype = "std::vector::bool";
|
|
||||||
} else {
|
|
||||||
//extraArgs[extraArgCount++] = sizeofTypeExpression(data.type);
|
|
||||||
//extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
|
|
||||||
}
|
|
||||||
} else if (outertype == "std::deque" || outertype == "deque") {
|
|
||||||
// remove 'std::allocator<...>':
|
|
||||||
extraArgs[1] = "0";
|
|
||||||
} else if (outertype == "std::stack" || outertype == "stack") {
|
|
||||||
// remove 'std::allocator<...>':
|
|
||||||
extraArgs[1] = "0";
|
|
||||||
} else if (outertype == "std::set" || outertype == "set") {
|
|
||||||
// remove 'std::less<...>':
|
|
||||||
extraArgs[1] = "0";
|
|
||||||
// remove 'std::allocator<...>':
|
|
||||||
extraArgs[2] = "0";
|
|
||||||
} else if (outertype == "std::map" || outertype == "map") {
|
|
||||||
// We don't want the comparator and the allocator confuse gdb.
|
|
||||||
// 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;
|
|
||||||
QString pairType = inners.at(3);
|
|
||||||
// remove 'std::allocator<...>':
|
|
||||||
pairType = pairType.mid(15, pairType.size() - 15 - 2);
|
|
||||||
extraArgs[2] = "(size_t)&(('" + pairType + "'*)0)->second";
|
|
||||||
extraArgs[3] = "0";
|
|
||||||
} else if (outertype == "std::basic_string" || outertype == "basic_string") {
|
|
||||||
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
|
|
||||||
if (inners.at(0) == "char") {
|
|
||||||
outertype = "std::string";
|
|
||||||
} else if (inners.at(0) == "wchar_t") {
|
|
||||||
outertype = "std::wstring";
|
|
||||||
}
|
|
||||||
extraArgs[0] = "0";
|
|
||||||
extraArgs[1] = "0";
|
|
||||||
extraArgs[2] = "0";
|
|
||||||
extraArgs[3] = "0";
|
|
||||||
}
|
|
||||||
|
|
||||||
//int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
|
//int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
|
||||||
//int protocol = data.iname.startsWith("watch") ? 3 : 2;
|
//int protocol = data.iname.startsWith("watch") ? 3 : 2;
|
||||||
int protocol = 2;
|
const int protocol = 2;
|
||||||
//int protocol = isDisplayedIName(data.iname) ? 3 : 2;
|
//int protocol = isDisplayedIName(data.iname) ? 3 : 2;
|
||||||
|
|
||||||
QString addr;
|
QString addr;
|
||||||
if (data.addr.startsWith("0x"))
|
if (data.addr.startsWith(QLatin1String("0x"))) {
|
||||||
addr = "(void*)" + data.addr;
|
addr = QLatin1String("(void*)") + data.addr;
|
||||||
else
|
} else {
|
||||||
addr = "&(" + data.exp + ")";
|
addr = QLatin1String("&(") + data.exp + QLatin1Char(')');
|
||||||
|
}
|
||||||
QByteArray params;
|
|
||||||
params.append(outertype.toUtf8());
|
|
||||||
params.append('\0');
|
|
||||||
params.append(data.iname.toUtf8());
|
|
||||||
params.append('\0');
|
|
||||||
params.append(data.exp.toUtf8());
|
|
||||||
params.append('\0');
|
|
||||||
params.append(inner.toUtf8());
|
|
||||||
params.append('\0');
|
|
||||||
params.append(data.iname.toUtf8());
|
|
||||||
params.append('\0');
|
|
||||||
|
|
||||||
sendWatchParameters(params);
|
sendWatchParameters(params);
|
||||||
|
|
||||||
QString cmd ="call "
|
QString cmd;
|
||||||
+ QString("(void*)qDumpObjectData440(")
|
QTextStream(&cmd) << "call " << "(void*)qDumpObjectData440(" <<
|
||||||
+ QString::number(protocol)
|
protocol << ',' << "%1+1" // placeholder for token
|
||||||
+ ',' + "%1+1" // placeholder for token
|
<<',' << addr << ',' << (dumpChildren ? "1" : "0")
|
||||||
+ ',' + addr
|
<< ',' << extraArgs.join(QString(QLatin1Char(','))) << ')';
|
||||||
+ ',' + (dumpChildren ? "1" : "0")
|
|
||||||
+ ',' + extraArgs[0]
|
|
||||||
+ ',' + extraArgs[1]
|
|
||||||
+ ',' + extraArgs[2]
|
|
||||||
+ ',' + extraArgs[3] + ')';
|
|
||||||
|
|
||||||
//qDebug() << "CMD: " << cmd;
|
qDebug() << "CMD: " << cmd;
|
||||||
|
|
||||||
QVariant var;
|
QVariant var;
|
||||||
var.setValue(data);
|
var.setValue(data);
|
||||||
@@ -3451,6 +3294,7 @@ void GdbEngine::updateWatchModel2()
|
|||||||
|
|
||||||
void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record)
|
void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record)
|
||||||
{
|
{
|
||||||
|
m_dumperHelper.clear();
|
||||||
//qDebug() << "DATA DUMPER TRIAL:" << record.toString();
|
//qDebug() << "DATA DUMPER TRIAL:" << record.toString();
|
||||||
GdbMi output = record.data.findChild("consolestreamoutput");
|
GdbMi output = record.data.findChild("consolestreamoutput");
|
||||||
QByteArray out = output.data();
|
QByteArray out = output.data();
|
||||||
@@ -3464,23 +3308,27 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record)
|
|||||||
GdbMi contents;
|
GdbMi contents;
|
||||||
contents.fromString(out);
|
contents.fromString(out);
|
||||||
GdbMi simple = contents.findChild("dumpers");
|
GdbMi simple = contents.findChild("dumpers");
|
||||||
m_namespace = contents.findChild("namespace").data();
|
|
||||||
|
m_dumperHelper.setQtNamespace(contents.findChild("namespace").data());
|
||||||
GdbMi qtversion = contents.findChild("qtversion");
|
GdbMi qtversion = contents.findChild("qtversion");
|
||||||
|
int qtv = 0;
|
||||||
if (qtversion.children().size() == 3) {
|
if (qtversion.children().size() == 3) {
|
||||||
m_qtVersion = (qtversion.childAt(0).data().toInt() << 16)
|
qtv = (qtversion.childAt(0).data().toInt() << 16)
|
||||||
+ (qtversion.childAt(1).data().toInt() << 8)
|
+ (qtversion.childAt(1).data().toInt() << 8)
|
||||||
+ qtversion.childAt(2).data().toInt();
|
+ qtversion.childAt(2).data().toInt();
|
||||||
//qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion;
|
//qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion;
|
||||||
} else {
|
|
||||||
m_qtVersion = 0;
|
|
||||||
}
|
}
|
||||||
|
m_dumperHelper.setQtVersion(qtv);
|
||||||
|
|
||||||
//qDebug() << "CONTENTS: " << contents.toString();
|
//qDebug() << "CONTENTS: " << contents.toString();
|
||||||
//qDebug() << "SIMPLE DUMPERS: " << simple.toString();
|
//qDebug() << "SIMPLE DUMPERS: " << simple.toString();
|
||||||
m_availableSimpleDebuggingHelpers.clear();
|
|
||||||
|
QStringList availableSimpleDebuggingHelpers;
|
||||||
foreach (const GdbMi &item, simple.children())
|
foreach (const GdbMi &item, simple.children())
|
||||||
m_availableSimpleDebuggingHelpers.append(item.data());
|
availableSimpleDebuggingHelpers.append(item.data());
|
||||||
if (m_availableSimpleDebuggingHelpers.isEmpty()) {
|
m_dumperHelper.parseQueryTypes(availableSimpleDebuggingHelpers, QtDumperHelper::GdbDebugger);
|
||||||
|
|
||||||
|
if (availableSimpleDebuggingHelpers.isEmpty()) {
|
||||||
m_debuggingHelperState = DebuggingHelperUnavailable;
|
m_debuggingHelperState = DebuggingHelperUnavailable;
|
||||||
q->showStatusMessage(tr("Debugging helpers not found."));
|
q->showStatusMessage(tr("Debugging helpers not found."));
|
||||||
//QMessageBox::warning(q->mainWindow(),
|
//QMessageBox::warning(q->mainWindow(),
|
||||||
@@ -3494,8 +3342,9 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record)
|
|||||||
} else {
|
} else {
|
||||||
m_debuggingHelperState = DebuggingHelperAvailable;
|
m_debuggingHelperState = DebuggingHelperAvailable;
|
||||||
q->showStatusMessage(tr("%1 custom dumpers found.")
|
q->showStatusMessage(tr("%1 custom dumpers found.")
|
||||||
.arg(m_availableSimpleDebuggingHelpers.size()));
|
.arg(m_dumperHelper.typeCount()));
|
||||||
}
|
}
|
||||||
|
qDebug() << m_dumperHelper.toString(true);
|
||||||
//qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDebuggingHelpers;
|
//qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDebuggingHelpers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -33,6 +33,7 @@
|
|||||||
#include "idebuggerengine.h"
|
#include "idebuggerengine.h"
|
||||||
#include "gdbmi.h"
|
#include "gdbmi.h"
|
||||||
#include "outputcollector.h"
|
#include "outputcollector.h"
|
||||||
|
#include "watchutils.h"
|
||||||
|
|
||||||
#include <consoleprocess.h>
|
#include <consoleprocess.h>
|
||||||
|
|
||||||
@@ -342,9 +343,7 @@ private:
|
|||||||
QString m_editedData;
|
QString m_editedData;
|
||||||
int m_pendingRequests;
|
int m_pendingRequests;
|
||||||
|
|
||||||
QStringList m_availableSimpleDebuggingHelpers;
|
QtDumperHelper m_dumperHelper;
|
||||||
QString m_namespace; // namespace used in "namespaced Qt";
|
|
||||||
int m_qtVersion; // Qt version used in the debugged program
|
|
||||||
|
|
||||||
DebuggingHelperState m_debuggingHelperState;
|
DebuggingHelperState m_debuggingHelperState;
|
||||||
QList<GdbMi> m_currentFunctionArgs;
|
QList<GdbMi> m_currentFunctionArgs;
|
||||||
|
@@ -78,6 +78,7 @@ static int watcherCounter = 0;
|
|||||||
WatchData::WatchData() :
|
WatchData::WatchData() :
|
||||||
childCount(-1),
|
childCount(-1),
|
||||||
valuedisabled(false),
|
valuedisabled(false),
|
||||||
|
source(0),
|
||||||
state(InitialState),
|
state(InitialState),
|
||||||
parentIndex(-1),
|
parentIndex(-1),
|
||||||
row(-1),
|
row(-1),
|
||||||
|
@@ -124,6 +124,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
int source; // Used by some debuggers (CDB) to tell where it originates from (dumper or symbol evaluation)
|
||||||
int state;
|
int state;
|
||||||
|
|
||||||
// Model
|
// Model
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -31,15 +31,17 @@
|
|||||||
#define WATCHUTILS_H
|
#define WATCHUTILS_H
|
||||||
|
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QMap>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QString;
|
class QDebug;
|
||||||
class QByteArray;
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class WatchData;
|
||||||
|
|
||||||
QString dotEscape(QString str);
|
QString dotEscape(QString str);
|
||||||
QString currentTime();
|
QString currentTime();
|
||||||
bool isSkippableFunction(const QString &funcName, const QString &fileName);
|
bool isSkippableFunction(const QString &funcName, const QString &fileName);
|
||||||
@@ -64,8 +66,131 @@ QString extractTypeFromPTypeOutput(const QString &str);
|
|||||||
bool isIntOrFloatType(const QString &type);
|
bool isIntOrFloatType(const QString &type);
|
||||||
QString sizeofTypeExpression(const QString &type);
|
QString sizeofTypeExpression(const QString &type);
|
||||||
|
|
||||||
// Parse 'query' (1) protocol response of the custom dumpers
|
// Decode string data as returned by the dumper helpers.
|
||||||
bool parseQueryDumperOutput(const QByteArray &a, QStringList *types, QString *qtVersion, QString *qtNamespace);
|
QString decodeData(const QByteArray &baIn, int encoding);
|
||||||
|
|
||||||
|
// Result of a dumper call.
|
||||||
|
struct QtDumperResult
|
||||||
|
{
|
||||||
|
struct Child {
|
||||||
|
Child();
|
||||||
|
|
||||||
|
int valueEncoded;
|
||||||
|
QString name;
|
||||||
|
QString address;
|
||||||
|
QByteArray value;
|
||||||
|
};
|
||||||
|
|
||||||
|
QtDumperResult();
|
||||||
|
void clear();
|
||||||
|
QList<WatchData> toWatchData(int source = 0) const;
|
||||||
|
|
||||||
|
QString iname;
|
||||||
|
QString address;
|
||||||
|
QString type;
|
||||||
|
QByteArray value;
|
||||||
|
int valueEncoded;
|
||||||
|
bool valuedisabled;
|
||||||
|
int childCount;
|
||||||
|
bool internal;
|
||||||
|
QString childType;
|
||||||
|
QList <Child> children;
|
||||||
|
};
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug in, const QtDumperResult &d);
|
||||||
|
|
||||||
|
/* Attempt to put common code of the dumper handling into a helper
|
||||||
|
* class.
|
||||||
|
* "Custom dumper" is a library compiled against the current
|
||||||
|
* Qt containing functions to evaluate values of Qt classes
|
||||||
|
* (such as QString, taking pointers to their addresses).
|
||||||
|
* The library must be loaded into the debuggee.
|
||||||
|
* It provides a function that takes input from an input buffer
|
||||||
|
* and some parameters and writes output into an output buffer.
|
||||||
|
* Parameter 1 is the protocol:
|
||||||
|
* 1) Query. Fills output buffer with known types, Qt version and namespace.
|
||||||
|
* This information is parsed and stored by this class (special type
|
||||||
|
* enumeration).
|
||||||
|
* 2) Evaluate symbol, taking address and some additional parameters
|
||||||
|
* depending on type. */
|
||||||
|
|
||||||
|
class QtDumperHelper {
|
||||||
|
public:
|
||||||
|
enum Debugger {
|
||||||
|
GdbDebugger, // Can evalulate expressions in function calls
|
||||||
|
CdbDebugger // Can only handle scalar, simple types in function calls
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Type {
|
||||||
|
UnknownType,
|
||||||
|
SupportedType, // A type that requires no special handling by the dumper
|
||||||
|
// Below types require special handling
|
||||||
|
QObjectType, QWidgetType, QObjectSlotType, QObjectSignalType,
|
||||||
|
QVectorType, QMapType, QMultiMapType, QMapNodeType,
|
||||||
|
StdVectorType, StdDequeType, StdSetType,StdMapType, StdStackType,
|
||||||
|
StdStringType
|
||||||
|
};
|
||||||
|
|
||||||
|
// Type/Parameter struct required for building a value query
|
||||||
|
struct TypeData {
|
||||||
|
TypeData();
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
bool isTemplate;
|
||||||
|
QString tmplate;
|
||||||
|
QString inner;
|
||||||
|
};
|
||||||
|
|
||||||
|
QtDumperHelper();
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
int typeCount() const;
|
||||||
|
// Look up a simple, non-template type
|
||||||
|
Type simpleType(const QString &simpleType) const;
|
||||||
|
// Look up a (potentially) template type and fill parameter struct
|
||||||
|
TypeData typeData(const QString &typeName) const;
|
||||||
|
Type type(const QString &typeName) const;
|
||||||
|
|
||||||
|
int qtVersion() const;
|
||||||
|
QString qtVersionString() const;
|
||||||
|
void setQtVersion(int v);
|
||||||
|
void setQtVersion(const QString &v);
|
||||||
|
|
||||||
|
QString qtNamespace() const;
|
||||||
|
void setQtNamespace(const QString &qtNamespace);
|
||||||
|
|
||||||
|
// Complete parse of "query" (protocol 1) response from debuggee buffer.
|
||||||
|
// 'data' excludes the leading indicator character.
|
||||||
|
bool parseQuery(const char *data, Debugger debugger);
|
||||||
|
// Set up from pre-parsed type list
|
||||||
|
void parseQueryTypes(const QStringList &l, Debugger debugger);
|
||||||
|
|
||||||
|
// Determine the parameters required for an "evaluate" (protocol 2) call
|
||||||
|
void evaluationParameters(const WatchData &data,
|
||||||
|
const TypeData &td,
|
||||||
|
Debugger debugger,
|
||||||
|
QByteArray *inBuffer,
|
||||||
|
QStringList *extraParameters) const;
|
||||||
|
|
||||||
|
// Parse the value response (protocol 2) from debuggee buffer.
|
||||||
|
// 'data' excludes the leading indicator character.
|
||||||
|
static bool parseValue(const char *data, QtDumperResult *r);
|
||||||
|
|
||||||
|
static bool needsExpressionSyntax(Type t);
|
||||||
|
|
||||||
|
QString toString(bool debug = false) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef QMap<QString, Type> NameTypeMap;
|
||||||
|
|
||||||
|
// Look up a simple (namespace) type
|
||||||
|
static Type specialType(QString s);
|
||||||
|
|
||||||
|
NameTypeMap m_nameTypeMap;
|
||||||
|
int m_qtVersion;
|
||||||
|
QString m_qtNamespace;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
Reference in New Issue
Block a user