Commit 013437cf authored by hjk's avatar hjk

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.
parent ac8e3714
......@@ -62,9 +62,10 @@ int qtGhVersion = QT_VERSION;
#endif
#if USE_QT_GUI
# include <QtGui/QWidget>
# include <QtGui/QPixmap>
# include <QtGui/QApplication>
# include <QtGui/QImage>
# include <QtGui/QPixmap>
# include <QtGui/QWidget>
#endif
#ifdef Q_OS_WIN
......@@ -1457,7 +1458,9 @@ static void qDumpQHashNode(QDumper &d)
static void qDumpQImage(QDumper &d)
{
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("numchild", "1");
if (d.dumpChildren) {
......@@ -2234,7 +2237,9 @@ static void qDumpQObjectSlotList(QDumper &d)
static void qDumpQPixmap(QDumper &d)
{
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("numchild", "0");
d.disarm();
......@@ -3023,6 +3028,14 @@ static void handleProtocolVersion2and3(QDumper & d)
} // 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
void *qDumpObjectData440(
int protocolVersion,
......@@ -3115,7 +3128,7 @@ void *qDumpObjectData440(
.put(""NS"QStringList=\"").put(sizeof(QStringList)).put("\",")
.put(""NS"QObject=\"").put(sizeof(QObject)).put("\",")
#if USE_QT_GUI
.put(""NS"QWidget=\"").put(sizeof(QWidget)<< "\",")
.put(""NS"QWidget=\"").put(sizeof(QWidget)).put("\",")
#endif
#ifdef Q_OS_WIN
.put("string=\"").put(sizeof(std::string)).put("\",")
......
TEMPLATE = lib
CONFIG += shared
QT = core
linux-* {
CONFIG -= release
CONFIG += debug
}
SOURCES=gdbmacros.cpp
true {
false {
DEFINES += USE_QT_GUI=0
QT = core
} else {
......
......@@ -165,6 +165,9 @@ DebuggerSettings *DebuggerSettings::instance()
item = new SavedAction(instance);
instance->insertItem(AssignType, item);
item = new SavedAction(instance);
instance->insertItem(WatchPoint, item);
//
// DebuggingHelper
//
......
......@@ -94,7 +94,7 @@ enum DebuggerActionCode
WatchExpression,
WatchExpressionInWindow,
RemoveWatchExpression,
WatchModelUpdate,
WatchPoint,
UseToolTips,
AssignValue,
AssignType,
......
......@@ -434,7 +434,8 @@ void DebuggerManager::init()
connect(theDebuggerAction(ExecuteCommand), SIGNAL(triggered()),
this, SLOT(executeDebuggerCommand()));
connect(theDebuggerAction(WatchPoint), SIGNAL(triggered()),
this, SLOT(watchPoint()));
m_breakDock = createDockForWidget(m_breakWindow);
......@@ -1089,6 +1090,13 @@ void DebuggerManager::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()
{
if (QAction *action = qobject_cast<QAction *>(sender()))
......
......@@ -318,6 +318,8 @@ public slots:
void executeDebuggerCommand();
void executeDebuggerCommand(const QString &command);
void watchPoint();
void showStatusMessage(const QString &msg, int timeout = -1); // -1 forever
private slots:
......
......@@ -103,6 +103,42 @@ static int &currentToken()
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
......@@ -2823,6 +2859,7 @@ void GdbEngine::setUseDebuggingHelpers(const QVariant &on)
//qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
// FIXME: a bit too harsh, but otherwise the treeview sometimes look funny
//m_expandedINames.clear();
Q_UNUSED(on);
setTokenBarrier();
updateLocals();
}
......@@ -3111,16 +3148,9 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record, const
{
m_dumperHelper.clear();
//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;
contents.fromString(out);
QTC_ASSERT(parseConsoleStream(record, &contents), /**/);
GdbMi simple = contents.findChild("dumpers");
m_dumperHelper.setQtNamespace(_(contents.findChild("namespace").data()));
......@@ -3281,11 +3311,7 @@ void GdbEngine::handleDebuggingHelperValue1(const GdbResultRecord &record,
if (record.resultClass == GdbResultDone) {
// ignore this case, data will follow
} 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());
//qDebug() << "CUSTOM DUMPER ERROR MESSAGE:" << msg;
#ifdef QT_DEBUG
// Make debugging of dumpers easier
if (theDebuggerBoolSetting(DebugDebuggingHelpers)
......@@ -3296,12 +3322,6 @@ void GdbEngine::handleDebuggingHelperValue1(const GdbResultRecord &record,
return;
}
#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>();
QTC_ASSERT(data.isValid(), return);
//qDebug() << "CUSTOM VALUE RESULT:" << record.toString();
//qDebug() << "FOR DATA:" << data.toString() << record.resultClass;
if (record.resultClass != GdbResultDone) {
......@@ -3317,26 +3338,8 @@ void GdbEngine::handleDebuggingHelperValue2(const GdbResultRecord &record,
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;
contents.fromString(out);
//qDebug() << "CONTENTS" << contents.toString(true);
if (!contents.isValid()) {
if (!parseConsoleStream(record, &contents)) {
data.setError(strNotInScope);
insertData(data);
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
void GdbEngine::handleChangedItem(QStandardItem *item)
{
......@@ -3933,6 +3882,29 @@ bool GdbEngine::startModeAllowsDumpers() const
|| 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)
{
opts->push_back(new GdbOptionsPage);
......
......@@ -115,6 +115,7 @@ private:
void assignValueInDebugger(const QString &expr, const QString &value);
void executeDebuggerCommand(const QString & command);
void watchPoint(const QPoint &);
void loadSymbols(const QString &moduleName);
void loadAllSymbols();
......@@ -220,6 +221,7 @@ private:
void handleExit(const GdbResultRecord &, const QVariant &);
void handleSetTargetAsync(const GdbResultRecord &, const QVariant &);
void handleTargetRemote(const GdbResultRecord &, const QVariant &);
void handleWatchPoint(const GdbResultRecord &, const QVariant &);
void debugMessage(const QString &msg);
bool showToolTip();
......
......@@ -93,6 +93,8 @@ public:
virtual void reloadSourceFiles() = 0;
virtual void reloadFullStack() = 0;
virtual void watchPoint(const QPoint &) {}
};
} // namespace Internal
......
......@@ -114,6 +114,8 @@ public:
WatchWindow::WatchWindow(Type type, QWidget *parent)
: QTreeView(parent), m_alwaysResizeColumnsToContents(true), m_type(type)
{
m_grabbing = false;
QAction *act = theDebuggerAction(UseAlternatingRowColors);
setWindowTitle(tr("Locals and Watchers"));
setAlternatingRowColors(act->isChecked());
......@@ -219,6 +221,8 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
QAction *act3 = new QAction(tr("Insert new watch item"), &menu);
menu.addAction(act3);
QAction *act4 = new QAction(tr("Select widget to watch"), &menu);
menu.addAction(act4);
menu.addSeparator();
menu.addAction(theDebuggerAction(RecheckDebuggingHelpers));
......@@ -233,7 +237,12 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
else if (act == act2)
setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
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()
......@@ -253,6 +262,17 @@ void WatchWindow::setAlwaysResizeColumnsToContents(bool on)
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)
{
Q_UNUSED(idx); // FIXME
......
......@@ -69,13 +69,14 @@ private:
void dragEnterEvent(QDragEnterEvent *ev);
void dropEvent(QDropEvent *ev);
void dragMoveEvent(QDragMoveEvent *ev);
bool event(QEvent *ev);
void editItem(const QModelIndex &idx);
void resetHelper(const QModelIndex &idx);
bool m_alwaysResizeColumnsToContents;
Type m_type;
bool m_grabbing;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment