debugger: add widget watchers by mouse click in the debugged application.

The option is hidden in the context menu.
Currently the application must be stopped.
This commit is contained in:
hjk
2009-07-01 12:49:41 +02:00
parent ac8e371486
commit 013437cfab
11 changed files with 124 additions and 102 deletions

View File

@@ -62,9 +62,10 @@ int qtGhVersion = QT_VERSION;
#endif #endif
#if USE_QT_GUI #if USE_QT_GUI
# include <QtGui/QWidget> # include <QtGui/QApplication>
# include <QtGui/QPixmap>
# include <QtGui/QImage> # include <QtGui/QImage>
# include <QtGui/QPixmap>
# include <QtGui/QWidget>
#endif #endif
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@@ -1457,7 +1458,9 @@ static void qDumpQHashNode(QDumper &d)
static void qDumpQImage(QDumper &d) static void qDumpQImage(QDumper &d)
{ {
const QImage &im = *reinterpret_cast<const QImage *>(d.data); const QImage &im = *reinterpret_cast<const QImage *>(d.data);
d.putItem("value", "(").put(im.width()).put("x").put(im.height()).put(")"); d.beginItem("value");
d.put("(").put(im.width()).put("x").put(im.height()).put(")");
d.endItem();
d.putItem("type", NS"QImage"); d.putItem("type", NS"QImage");
d.putItem("numchild", "1"); d.putItem("numchild", "1");
if (d.dumpChildren) { if (d.dumpChildren) {
@@ -2234,7 +2237,9 @@ static void qDumpQObjectSlotList(QDumper &d)
static void qDumpQPixmap(QDumper &d) static void qDumpQPixmap(QDumper &d)
{ {
const QPixmap &im = *reinterpret_cast<const QPixmap *>(d.data); const QPixmap &im = *reinterpret_cast<const QPixmap *>(d.data);
d.putItem("value", "(").put(im.width()).put("x").put(im.height()).put(")"); d.beginItem("value");
d.put("(").put(im.width()).put("x").put(im.height()).put(")");
d.endItem();
d.putItem("type", NS"QPixmap"); d.putItem("type", NS"QPixmap");
d.putItem("numchild", "0"); d.putItem("numchild", "0");
d.disarm(); d.disarm();
@@ -3023,6 +3028,14 @@ static void handleProtocolVersion2and3(QDumper & d)
} // anonymous namespace } // anonymous namespace
#if USE_QT_GUI
extern "C" Q_DECL_EXPORT
void *watchPoint(int x, int y)
{
return QApplication::widgetAt(x, y);
}
#endif
extern "C" Q_DECL_EXPORT extern "C" Q_DECL_EXPORT
void *qDumpObjectData440( void *qDumpObjectData440(
int protocolVersion, int protocolVersion,
@@ -3115,7 +3128,7 @@ void *qDumpObjectData440(
.put(""NS"QStringList=\"").put(sizeof(QStringList)).put("\",") .put(""NS"QStringList=\"").put(sizeof(QStringList)).put("\",")
.put(""NS"QObject=\"").put(sizeof(QObject)).put("\",") .put(""NS"QObject=\"").put(sizeof(QObject)).put("\",")
#if USE_QT_GUI #if USE_QT_GUI
.put(""NS"QWidget=\"").put(sizeof(QWidget)<< "\",") .put(""NS"QWidget=\"").put(sizeof(QWidget)).put("\",")
#endif #endif
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
.put("string=\"").put(sizeof(std::string)).put("\",") .put("string=\"").put(sizeof(std::string)).put("\",")

View File

@@ -1,13 +1,12 @@
TEMPLATE = lib TEMPLATE = lib
CONFIG += shared CONFIG += shared
QT = core
linux-* { linux-* {
CONFIG -= release CONFIG -= release
CONFIG += debug CONFIG += debug
} }
SOURCES=gdbmacros.cpp SOURCES=gdbmacros.cpp
true { false {
DEFINES += USE_QT_GUI=0 DEFINES += USE_QT_GUI=0
QT = core QT = core
} else { } else {

View File

@@ -165,6 +165,9 @@ DebuggerSettings *DebuggerSettings::instance()
item = new SavedAction(instance); item = new SavedAction(instance);
instance->insertItem(AssignType, item); instance->insertItem(AssignType, item);
item = new SavedAction(instance);
instance->insertItem(WatchPoint, item);
// //
// DebuggingHelper // DebuggingHelper
// //

View File

@@ -94,7 +94,7 @@ enum DebuggerActionCode
WatchExpression, WatchExpression,
WatchExpressionInWindow, WatchExpressionInWindow,
RemoveWatchExpression, RemoveWatchExpression,
WatchModelUpdate, WatchPoint,
UseToolTips, UseToolTips,
AssignValue, AssignValue,
AssignType, AssignType,

View File

@@ -434,7 +434,8 @@ void DebuggerManager::init()
connect(theDebuggerAction(ExecuteCommand), SIGNAL(triggered()), connect(theDebuggerAction(ExecuteCommand), SIGNAL(triggered()),
this, SLOT(executeDebuggerCommand())); this, SLOT(executeDebuggerCommand()));
connect(theDebuggerAction(WatchPoint), SIGNAL(triggered()),
this, SLOT(watchPoint()));
m_breakDock = createDockForWidget(m_breakWindow); m_breakDock = createDockForWidget(m_breakWindow);
@@ -1089,6 +1090,13 @@ void DebuggerManager::nextIExec()
m_engine->nextIExec(); m_engine->nextIExec();
} }
void DebuggerManager::watchPoint()
{
if (QAction *action = qobject_cast<QAction *>(sender()))
if (m_engine)
m_engine->watchPoint(action->data().toPoint());
}
void DebuggerManager::executeDebuggerCommand() void DebuggerManager::executeDebuggerCommand()
{ {
if (QAction *action = qobject_cast<QAction *>(sender())) if (QAction *action = qobject_cast<QAction *>(sender()))

View File

@@ -318,6 +318,8 @@ public slots:
void executeDebuggerCommand(); void executeDebuggerCommand();
void executeDebuggerCommand(const QString &command); void executeDebuggerCommand(const QString &command);
void watchPoint();
void showStatusMessage(const QString &msg, int timeout = -1); // -1 forever void showStatusMessage(const QString &msg, int timeout = -1); // -1 forever
private slots: private slots:

View File

@@ -103,6 +103,42 @@ static int &currentToken()
return token; return token;
} }
// reads a MI-encoded item frome the consolestream
static bool parseConsoleStream(const GdbResultRecord &record, GdbMi *contents)
{
GdbMi output = record.data.findChild("consolestreamoutput");
QByteArray out = output.data();
int markerPos = out.indexOf('"') + 1; // position of 'success marker'
if (markerPos == 0 || out.at(markerPos) == 'f') { // 't' or 'f'
// custom dumper produced no output
return false;
}
out = out.mid(markerPos + 1);
out = out.left(out.lastIndexOf('"'));
// optimization: dumper output never needs real C unquoting
out.replace('\\', "");
out = "dummy={" + out + "}";
contents->fromString(out);
//qDebug() << "CONTENTS" << contents->toString(true);
return contents->isValid();
}
static QByteArray parsePlainConsoleStream(const GdbResultRecord &record)
{
GdbMi output = record.data.findChild("consolestreamoutput");
QByteArray out = output.data();
// FIXME: proper decoding needed
if (out.endsWith("\\n"))
out.chop(2);
while (out.endsWith('\n') || out.endsWith(' '))
out.chop(1);
int pos = out.indexOf(" = ");
return out.mid(pos + 3);
}
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// //
// GdbEngine // GdbEngine
@@ -2823,6 +2859,7 @@ void GdbEngine::setUseDebuggingHelpers(const QVariant &on)
//qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on; //qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
// FIXME: a bit too harsh, but otherwise the treeview sometimes look funny // FIXME: a bit too harsh, but otherwise the treeview sometimes look funny
//m_expandedINames.clear(); //m_expandedINames.clear();
Q_UNUSED(on);
setTokenBarrier(); setTokenBarrier();
updateLocals(); updateLocals();
} }
@@ -3111,16 +3148,9 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record, const
{ {
m_dumperHelper.clear(); m_dumperHelper.clear();
//qDebug() << "DATA DUMPER TRIAL:" << record.toString(); //qDebug() << "DATA DUMPER TRIAL:" << record.toString();
GdbMi output = record.data.findChild("consolestreamoutput");
QByteArray out = output.data();
out = out.mid(out.indexOf('"') + 2); // + 1 is success marker
out = out.left(out.lastIndexOf('"'));
out.replace('\\', ""); // optimization: dumper output never needs real C unquoting
out = "dummy={" + out + "}";
//qDebug() << "OUTPUT:" << out;
GdbMi contents; GdbMi contents;
contents.fromString(out); QTC_ASSERT(parseConsoleStream(record, &contents), /**/);
GdbMi simple = contents.findChild("dumpers"); GdbMi simple = contents.findChild("dumpers");
m_dumperHelper.setQtNamespace(_(contents.findChild("namespace").data())); m_dumperHelper.setQtNamespace(_(contents.findChild("namespace").data()));
@@ -3281,11 +3311,7 @@ void GdbEngine::handleDebuggingHelperValue1(const GdbResultRecord &record,
if (record.resultClass == GdbResultDone) { if (record.resultClass == GdbResultDone) {
// ignore this case, data will follow // ignore this case, data will follow
} else if (record.resultClass == GdbResultError) { } else if (record.resultClass == GdbResultError) {
// Record an extra result, as the socket result will be lost
// in transmission
//--m_pendingRequests;
QString msg = QString::fromLocal8Bit(record.data.findChild("msg").data()); QString msg = QString::fromLocal8Bit(record.data.findChild("msg").data());
//qDebug() << "CUSTOM DUMPER ERROR MESSAGE:" << msg;
#ifdef QT_DEBUG #ifdef QT_DEBUG
// Make debugging of dumpers easier // Make debugging of dumpers easier
if (theDebuggerBoolSetting(DebugDebuggingHelpers) if (theDebuggerBoolSetting(DebugDebuggingHelpers)
@@ -3296,12 +3322,6 @@ void GdbEngine::handleDebuggingHelperValue1(const GdbResultRecord &record,
return; return;
} }
#endif #endif
//if (msg.startsWith("The program being debugged was sig"))
// msg = strNotInScope;
//if (msg.startsWith("The program being debugged stopped while"))
// msg = strNotInScope;
//data.setError(msg);
//insertData(data);
} }
} }
@@ -3310,6 +3330,7 @@ void GdbEngine::handleDebuggingHelperValue2(const GdbResultRecord &record,
{ {
WatchData data = cookie.value<WatchData>(); WatchData data = cookie.value<WatchData>();
QTC_ASSERT(data.isValid(), return); QTC_ASSERT(data.isValid(), return);
//qDebug() << "CUSTOM VALUE RESULT:" << record.toString(); //qDebug() << "CUSTOM VALUE RESULT:" << record.toString();
//qDebug() << "FOR DATA:" << data.toString() << record.resultClass; //qDebug() << "FOR DATA:" << data.toString() << record.resultClass;
if (record.resultClass != GdbResultDone) { if (record.resultClass != GdbResultDone) {
@@ -3317,26 +3338,8 @@ void GdbEngine::handleDebuggingHelperValue2(const GdbResultRecord &record,
return; return;
} }
GdbMi output = record.data.findChild("consolestreamoutput");
QByteArray out = output.data();
int markerPos = out.indexOf('"') + 1; // position of 'success marker'
if (markerPos == 0 || out.at(markerPos) == 'f') { // 't' or 'f'
// custom dumper produced no output
data.setError(strNotInScope);
insertData(data);
return;
}
out = out.mid(markerPos + 1);
out = out.left(out.lastIndexOf('"'));
out.replace('\\', ""); // optimization: dumper output never needs real C unquoting
out = "dummy={" + out + "}";
GdbMi contents; GdbMi contents;
contents.fromString(out); if (!parseConsoleStream(record, &contents)) {
//qDebug() << "CONTENTS" << contents.toString(true);
if (!contents.isValid()) {
data.setError(strNotInScope); data.setError(strNotInScope);
insertData(data); insertData(data);
return; return;
@@ -3759,60 +3762,6 @@ void GdbEngine::handleVarListChildren(const GdbResultRecord &record,
} }
} }
/*
void GdbEngine::handleToolTip(const GdbResultRecord &record,
const QVariant &cookie)
{
const QByteArray &what = cookie.toByteArray();
//qDebug() << "HANDLE TOOLTIP:" << what << m_toolTip.toString();
// << "record: " << record.toString();
if (record.resultClass == GdbResultError) {
if (what == "create") {
postCommand(_("ptype ") + m_toolTip.exp,
Discardable, CB(handleToolTip), QByteArray("ptype"));
return;
}
if (what == "evaluate") {
QByteArray msg = record.data.findChild("msg").data();
if (msg.startsWith("Cannot look up value of a typedef")) {
m_toolTip.value = tr("%1 is a typedef.").arg(m_toolTip.exp);
//return;
}
}
} else if (record.resultClass == GdbResultDone) {
if (what == "create") {
setWatchDataType(m_toolTip, record.data.findChild("type"));
setWatchDataChildCount(m_toolTip, record.data.findChild("numchild"));
if (hasDebuggingHelperForType(m_toolTip.type))
runDebuggingHelper(m_toolTip, false);
else
q->showStatusMessage(tr("Retrieving data for tooltip..."), 10000);
postCommand(_("-data-evaluate-expression ") + m_toolTip.exp,
Discardable, CB(handleToolTip), QByteArray("evaluate"));
return;
}
if (what == "evaluate") {
m_toolTip.value = m_toolTip.type + _c(' ') + m_toolTip.exp
+ _(" = " + record.data.findChild("value").data());
//return;
}
if (what == "ptype") {
GdbMi mi = record.data.findChild("consolestreamoutput");
m_toolTip.value = extractTypeFromPTypeOutput(_(mi.data()));
//return;
}
}
m_toolTip.iname = tooltipIName;
m_toolTip.setChildrenUnneeded();
m_toolTip.setHasChildrenUnneeded();
insertData(m_toolTip);
qDebug() << "DATA INSERTED";
QTimer::singleShot(0, this, SLOT(updateWatchModel2()));
qDebug() << "HANDLE TOOLTIP END";
}
*/
#if 0 #if 0
void GdbEngine::handleChangedItem(QStandardItem *item) void GdbEngine::handleChangedItem(QStandardItem *item)
{ {
@@ -3933,6 +3882,29 @@ bool GdbEngine::startModeAllowsDumpers() const
|| q->startMode() == AttachExternal; || q->startMode() == AttachExternal;
} }
void GdbEngine::watchPoint(const QPoint &pnt)
{
//qDebug() << "WATCH " << pnt;
postCommand(_("call (void*)watchPoint(%1,%2)").arg(pnt.x()).arg(pnt.y()),
NeedsStop, CB(handleWatchPoint));
}
void GdbEngine::handleWatchPoint(const GdbResultRecord &record, const QVariant &)
{
//qDebug() << "HANDLE WATCH POINT:" << record.toString();
if (record.resultClass == GdbResultDone) {
GdbMi contents = record.data.findChild("consolestreamoutput");
// "$5 = (void *) 0xbfa7ebfc\n"
QString str = _(parsePlainConsoleStream(record));
// "(void *) 0xbfa7ebfc"
QString addr = str.mid(9);
QString ns = m_dumperHelper.qtNamespace();
QString type = ns.isEmpty() ? _("QWidget*") : _("'%1QWidget'*").arg(ns);
QString exp = _("(*(%1)%2)").arg(type).arg(addr);
theDebuggerAction(WatchExpression)->trigger(exp);
}
}
IDebuggerEngine *createGdbEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *opts) IDebuggerEngine *createGdbEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *opts)
{ {
opts->push_back(new GdbOptionsPage); opts->push_back(new GdbOptionsPage);

View File

@@ -115,6 +115,7 @@ private:
void assignValueInDebugger(const QString &expr, const QString &value); void assignValueInDebugger(const QString &expr, const QString &value);
void executeDebuggerCommand(const QString & command); void executeDebuggerCommand(const QString & command);
void watchPoint(const QPoint &);
void loadSymbols(const QString &moduleName); void loadSymbols(const QString &moduleName);
void loadAllSymbols(); void loadAllSymbols();
@@ -220,6 +221,7 @@ private:
void handleExit(const GdbResultRecord &, const QVariant &); void handleExit(const GdbResultRecord &, const QVariant &);
void handleSetTargetAsync(const GdbResultRecord &, const QVariant &); void handleSetTargetAsync(const GdbResultRecord &, const QVariant &);
void handleTargetRemote(const GdbResultRecord &, const QVariant &); void handleTargetRemote(const GdbResultRecord &, const QVariant &);
void handleWatchPoint(const GdbResultRecord &, const QVariant &);
void debugMessage(const QString &msg); void debugMessage(const QString &msg);
bool showToolTip(); bool showToolTip();

View File

@@ -93,6 +93,8 @@ public:
virtual void reloadSourceFiles() = 0; virtual void reloadSourceFiles() = 0;
virtual void reloadFullStack() = 0; virtual void reloadFullStack() = 0;
virtual void watchPoint(const QPoint &) {}
}; };
} // namespace Internal } // namespace Internal

View File

@@ -114,6 +114,8 @@ public:
WatchWindow::WatchWindow(Type type, QWidget *parent) WatchWindow::WatchWindow(Type type, QWidget *parent)
: QTreeView(parent), m_alwaysResizeColumnsToContents(true), m_type(type) : QTreeView(parent), m_alwaysResizeColumnsToContents(true), m_type(type)
{ {
m_grabbing = false;
QAction *act = theDebuggerAction(UseAlternatingRowColors); QAction *act = theDebuggerAction(UseAlternatingRowColors);
setWindowTitle(tr("Locals and Watchers")); setWindowTitle(tr("Locals and Watchers"));
setAlternatingRowColors(act->isChecked()); setAlternatingRowColors(act->isChecked());
@@ -219,6 +221,8 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
QAction *act3 = new QAction(tr("Insert new watch item"), &menu); QAction *act3 = new QAction(tr("Insert new watch item"), &menu);
menu.addAction(act3); menu.addAction(act3);
QAction *act4 = new QAction(tr("Select widget to watch"), &menu);
menu.addAction(act4);
menu.addSeparator(); menu.addSeparator();
menu.addAction(theDebuggerAction(RecheckDebuggingHelpers)); menu.addAction(theDebuggerAction(RecheckDebuggingHelpers));
@@ -233,7 +237,12 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
else if (act == act2) else if (act == act2)
setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents); setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
else if (act == act3) else if (act == act3)
theDebuggerAction(WatchExpression)->trigger(WatchHandler::watcherEditPlaceHolder()); theDebuggerAction(WatchExpression)
->trigger(WatchHandler::watcherEditPlaceHolder());
else if (act == act4) {
grabMouse(Qt::CrossCursor);
m_grabbing = true;
}
} }
void WatchWindow::resizeColumnsToContents() void WatchWindow::resizeColumnsToContents()
@@ -253,6 +262,17 @@ void WatchWindow::setAlwaysResizeColumnsToContents(bool on)
header()->setResizeMode(1, mode); header()->setResizeMode(1, mode);
} }
bool WatchWindow::event(QEvent *ev)
{
if (m_grabbing && ev->type() == QEvent::MouseButtonPress) {
QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
m_grabbing = false;
releaseMouse();
theDebuggerAction(WatchPoint)->trigger(mapToGlobal(mev->pos()));
}
return QTreeView::event(ev);
}
void WatchWindow::editItem(const QModelIndex &idx) void WatchWindow::editItem(const QModelIndex &idx)
{ {
Q_UNUSED(idx); // FIXME Q_UNUSED(idx); // FIXME

View File

@@ -69,13 +69,14 @@ private:
void dragEnterEvent(QDragEnterEvent *ev); void dragEnterEvent(QDragEnterEvent *ev);
void dropEvent(QDropEvent *ev); void dropEvent(QDropEvent *ev);
void dragMoveEvent(QDragMoveEvent *ev); void dragMoveEvent(QDragMoveEvent *ev);
bool event(QEvent *ev);
void editItem(const QModelIndex &idx); void editItem(const QModelIndex &idx);
void resetHelper(const QModelIndex &idx); void resetHelper(const QModelIndex &idx);
bool m_alwaysResizeColumnsToContents; bool m_alwaysResizeColumnsToContents;
Type m_type; Type m_type;
bool m_grabbing;
}; };