Commit 6c0b947e authored by hjk's avatar hjk

debugger: first shot a implementing data watchpoints

parent 4648a5b3
......@@ -31,6 +31,7 @@
#include "debuggeractions.h"
#include "debuggermanager.h"
#include "debuggerstringutils.h"
#include "stackframe.h"
#include <texteditor/basetextmark.h>
......@@ -53,13 +54,11 @@ using namespace Debugger::Internal;
// Compare file names case insensitively on Windows.
static inline bool fileNameMatch(const QString &f1, const QString &f2)
{
return f1.compare(f2,
#ifdef Q_OS_WIN
Qt::CaseInsensitive
return f1.compare(f2, Qt::CaseInsensitive) == 0;
#else
Qt::CaseSensitive
return f1 == f2;
#endif
) == 0;
}
namespace Debugger {
......@@ -87,7 +86,7 @@ public:
QIcon icon() const
{
const BreakHandler *handler = DebuggerManager::instance()->breakHandler();
const BreakHandler *handler = m_data->handler();
if (!m_enabled)
return handler->disabledBreakpointIcon();
return m_pending ? handler->pendingBreakPointIcon() : handler->breakpointIcon();
......@@ -162,28 +161,21 @@ private:
//
//////////////////////////////////////////////////////////////////
BreakpointData::BreakpointData(BreakHandler *handler)
BreakpointData::BreakpointData()
{
//qDebug() << "CREATE BREAKPOINTDATA" << this;
m_handler = handler;
m_handler = 0;
enabled = true;
pending = true;
type = BreakpointType;
marker = 0;
m_markerLineNumber = 0;
bpMultiple = false;
//#if defined(Q_OS_MAC)
// // full names do not work on Mac/MI
useFullPath = false;
//#else
// //where = m_manager->shortName(data->fileName);
// useFullPath = true;
//#endif
}
BreakpointData::~BreakpointData()
{
removeMarker();
//qDebug() << "DESTROY BREAKPOINTDATA" << this;
}
void BreakpointData::removeMarker()
......@@ -227,8 +219,6 @@ QString BreakpointData::toToolTip() const
<< "</td><td>" << m_markerLineNumber << "</td></tr>"
<< "<tr><td>" << BreakHandler::tr("Breakpoint Number:")
<< "</td><td>" << bpNumber << "</td></tr>"
<< "<tr><td>" << BreakHandler::tr("Breakpoint Address:")
<< "</td><td>" << bpAddress << "</td></tr>"
<< "</table><br><hr><table>"
<< "<tr><th>" << BreakHandler::tr("Property")
<< "</th><th>" << BreakHandler::tr("Requested")
......@@ -241,6 +231,8 @@ QString BreakpointData::toToolTip() const
<< "</td><td>" << funcName << "</td><td>" << bpFuncName << "</td></tr>"
<< "<tr><td>" << BreakHandler::tr("Line Number:")
<< "</td><td>" << lineNumber << "</td><td>" << bpLineNumber << "</td></tr>"
<< "<tr><td>" << BreakHandler::tr("Breakpoint Address:")
<< "</td><td>" << address << "</td><td>" << bpAddress << "</td></tr>"
<< "<tr><td>" << BreakHandler::tr("Corrected Line Number:")
<< "</td><td>-</td><td>" << bpCorrectedLineNumber << "</td></tr>"
<< "<tr><td>" << BreakHandler::tr("Condition:")
......@@ -260,13 +252,14 @@ QString BreakpointData::toString() const
str << BreakHandler::tr("Marker File:") << m_markerFileName << ' '
<< BreakHandler::tr("Marker Line:") << m_markerLineNumber << ' '
<< BreakHandler::tr("Breakpoint Number:") << bpNumber << ' '
<< BreakHandler::tr("Breakpoint Address:") << bpAddress << '\n'
<< BreakHandler::tr("File Name:")
<< fileName << " -- " << bpFileName << '\n'
<< BreakHandler::tr("Function Name:")
<< funcName << " -- " << bpFuncName << '\n'
<< BreakHandler::tr("Line Number:")
<< lineNumber << " -- " << bpLineNumber << '\n'
<< BreakHandler::tr("Breakpoint Address:")
<< address << " -- " << bpAddress << '\n'
<< BreakHandler::tr("Condition:")
<< condition << " -- " << bpCondition << '\n'
<< BreakHandler::tr("Ignore Count:")
......@@ -310,9 +303,10 @@ bool BreakpointData::conditionsMatch() const
BreakHandler::BreakHandler(DebuggerManager *manager, QObject *parent) :
QAbstractTableModel(parent),
m_breakpointIcon(QLatin1String(":/debugger/images/breakpoint_16.png")),
m_disabledBreakpointIcon(QLatin1String(":/debugger/images/breakpoint_disabled_16.png")),
m_pendingBreakPointIcon(QLatin1String(":/debugger/images/breakpoint_pending_16.png")),
m_breakpointIcon(_(":/debugger/images/breakpoint_16.png")),
m_disabledBreakpointIcon(_(":/debugger/images/breakpoint_disabled_16.png")),
m_pendingBreakPointIcon(_(":/debugger/images/breakpoint_pending_16.png")),
m_watchpointIcon(_(":/debugger/images/watchpoint.png")),
m_manager(manager)
{
}
......@@ -412,22 +406,29 @@ void BreakHandler::saveBreakpoints()
for (int index = 0; index != size(); ++index) {
const BreakpointData *data = at(index);
QMap<QString, QVariant> map;
// Do not persist Watchpoints.
//if (data->type == BreakpointData::WatchpointType)
// continue;
if (data->type != BreakpointData::BreakpointType)
map.insert(_("type"), data->type);
if (!data->fileName.isEmpty())
map.insert(QLatin1String("filename"), data->fileName);
map.insert(_("filename"), data->fileName);
if (!data->lineNumber.isEmpty())
map.insert(QLatin1String("linenumber"), data->lineNumber);
map.insert(_("linenumber"), data->lineNumber);
if (!data->funcName.isEmpty())
map.insert(QLatin1String("funcname"), data->funcName);
map.insert(_("funcname"), data->funcName);
if (!data->address.isEmpty())
map.insert(_("address"), data->address);
if (!data->condition.isEmpty())
map.insert(QLatin1String("condition"), data->condition);
map.insert(_("condition"), data->condition);
if (!data->ignoreCount.isEmpty())
map.insert(QLatin1String("ignorecount"), data->ignoreCount);
map.insert(_("ignorecount"), data->ignoreCount);
if (!data->threadSpec.isEmpty())
map.insert(QLatin1String("threadspec"), data->threadSpec);
map.insert(_("threadspec"), data->threadSpec);
if (!data->enabled)
map.insert(QLatin1String("disabled"), QLatin1String("1"));
map.insert(_("disabled"), _("1"));
if (data->useFullPath)
map.insert(QLatin1String("usefullpath"), QLatin1String("1"));
map.insert(_("usefullpath"), _("1"));
list.append(map);
}
m_manager->setSessionValue("Breakpoints", list);
......@@ -440,31 +441,37 @@ void BreakHandler::loadBreakpoints()
clear();
foreach (const QVariant &var, list) {
const QMap<QString, QVariant> map = var.toMap();
BreakpointData *data = new BreakpointData(this);
QVariant v = map.value(QLatin1String("filename"));
BreakpointData *data = new BreakpointData;
QVariant v = map.value(_("filename"));
if (v.isValid())
data->fileName = v.toString();
v = map.value(QLatin1String("linenumber"));
v = map.value(_("linenumber"));
if (v.isValid())
data->lineNumber = v.toString().toLatin1();
v = map.value(QLatin1String("condition"));
v = map.value(_("condition"));
if (v.isValid())
data->condition = v.toString().toLatin1();
v = map.value(QLatin1String("ignorecount"));
v = map.value(_("address"));
if (v.isValid())
data->address = v.toString().toLatin1();
v = map.value(_("ignorecount"));
if (v.isValid())
data->ignoreCount = v.toString().toLatin1();
v = map.value(QLatin1String("threadspec"));
v = map.value(_("threadspec"));
if (v.isValid())
data->threadSpec = v.toString().toLatin1();
v = map.value(QLatin1String("funcname"));
v = map.value(_("funcname"));
if (v.isValid())
data->funcName = v.toString();
v = map.value(QLatin1String("disabled"));
v = map.value(_("disabled"));
if (v.isValid())
data->enabled = !v.toInt();
v = map.value(QLatin1String("usefullpath"));
v = map.value(_("usefullpath"));
if (v.isValid())
data->useFullPath = bool(v.toInt());
v = map.value(_("type"));
if (v.isValid())
data->type = BreakpointData::Type(v.toInt());
data->setMarkerFileName(data->fileName);
data->setMarkerLineNumber(data->lineNumber.toInt());
append(data);
......@@ -538,6 +545,8 @@ QVariant BreakHandler::data(const QModelIndex &mi, int role) const
if (role == Qt::UserRole)
return data->enabled;
if (role == Qt::DecorationRole) {
if (data->type == BreakpointData::WatchpointType)
return m_watchpointIcon;
if (!data->enabled)
return m_disabledBreakpointIcon;
return data->pending ? m_pendingBreakPointIcon : m_breakpointIcon;
......@@ -606,8 +615,11 @@ QVariant BreakHandler::data(const QModelIndex &mi, int role) const
if (role == Qt::UserRole + 1)
return data->threadSpec;
case 7:
if (role == Qt::DisplayRole)
if (role == Qt::DisplayRole) {
if (data->type == BreakpointData::WatchpointType)
return data->address;
return data->bpAddress;
}
break;
}
if (role == Qt::ToolTipRole)
......@@ -706,6 +718,7 @@ bool BreakHandler::setData(const QModelIndex &mi, const QVariant &value, int rol
void BreakHandler::append(BreakpointData *data)
{
data->m_handler = this;
m_bp.append(data);
m_inserted.append(data);
}
......@@ -823,7 +836,7 @@ void BreakHandler::breakByFunction(const QString &functionName)
&& data->ignoreCount.isEmpty())
return;
}
BreakpointData *data = new BreakpointData(this);
BreakpointData *data = new BreakpointData;
data->funcName = functionName;
append(data);
saveBreakpoints();
......
......@@ -30,90 +30,15 @@
#ifndef DEBUGGER_BREAKHANDLER_H
#define DEBUGGER_BREAKHANDLER_H
#include "breakpoint.h"
#include <QtCore/QObject>
#include <QtCore/QAbstractTableModel>
#include <QtGui/QIcon>
namespace Debugger {
class DebuggerManager;
namespace Internal {
class BreakpointMarker;
class BreakHandler;
//////////////////////////////////////////////////////////////////
//
// BreakpointData
//
//////////////////////////////////////////////////////////////////
class BreakpointData
{
public:
explicit BreakpointData(BreakHandler *handler);
~BreakpointData();
void removeMarker();
void updateMarker();
QString toToolTip() const;
QString toString() const;
BreakHandler *handler() { return m_handler; }
bool isLocatedAt(const QString &fileName, int lineNumber) const;
bool conditionsMatch() const;
private:
// Intentionally unimplemented.
// Making it copyable is tricky because of the markers.
void operator=(const BreakpointData &);
BreakpointData(const BreakpointData &);
// Our owner
BreakHandler *m_handler; // Not owned.
public:
bool enabled; // Should we talk to the debugger engine?
bool pending; // Does the debugger engine know about us already?
// This "user requested information" will get stored in the session.
QString fileName; // Short name of source file.
QByteArray condition; // Condition associated with breakpoint.
QByteArray ignoreCount; // Ignore count associated with breakpoint.
QByteArray lineNumber; // Line in source file.
QByteArray threadSpec; // Thread specification.
QString funcName; // Name of containing function.
bool useFullPath; // Should we use the full path when setting the bp?
// This is what gdb produced in response.
QByteArray bpNumber; // Breakpoint number assigned by the debugger engine.
QByteArray bpCondition; // Condition acknowledged by the debugger engine.
QByteArray bpIgnoreCount;// Ignore count acknowledged by the debugger engine.
QString bpFileName; // File name acknowledged by the debugger engine.
QString bpFullName; // Full file name acknowledged by the debugger engine.
QByteArray bpLineNumber; // Line number acknowledged by the debugger engine.
QByteArray bpCorrectedLineNumber; // Acknowledged by the debugger engine.
QByteArray bpThreadSpec; // Thread spec acknowledged by the debugger engine.
QString bpFuncName; // Function name acknowledged by the debugger engine.
QByteArray bpAddress; // Address acknowledged by the debugger engine.
bool bpMultiple; // Happens in constructors/gdb.
bool bpEnabled; // Enable/disable command sent.
void setMarkerFileName(const QString &fileName);
QString markerFileName() const { return m_markerFileName; }
void setMarkerLineNumber(int lineNumber);
int markerLineNumber() const { return m_markerLineNumber; }
private:
// Taken from either user input or gdb responses.
QString m_markerFileName; // Used to locate the marker.
int m_markerLineNumber;
// Our red blob in the editor.
BreakpointMarker *marker;
};
//////////////////////////////////////////////////////////////////
//
// BreakHandler
......@@ -184,6 +109,7 @@ private:
const QIcon m_breakpointIcon;
const QIcon m_disabledBreakpointIcon;
const QIcon m_pendingBreakPointIcon;
const QIcon m_watchpointIcon;
DebuggerManager *m_manager; // Not owned.
QList<BreakpointData *> m_bp;
......
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef DEBUGGER_BREAKPOINT_H
#define DEBUGGER_BREAKPOINT_H
#include <QtCore/QString>
namespace Debugger {
class DebuggerManager;
namespace Internal {
class BreakpointMarker;
class BreakHandler;
//////////////////////////////////////////////////////////////////
//
// BreakpointData
//
//////////////////////////////////////////////////////////////////
class BreakpointData
{
public:
BreakpointData();
~BreakpointData();
void removeMarker();
void updateMarker();
QString toToolTip() const;
QString toString() const;
BreakHandler *handler() { return m_handler; }
bool isLocatedAt(const QString &fileName, int lineNumber) const;
bool conditionsMatch() const;
private:
// Intentionally unimplemented.
// Making it copyable is tricky because of the markers.
void operator=(const BreakpointData &);
BreakpointData(const BreakpointData &);
// Our owner
BreakHandler *m_handler; // Not owned.
friend class BreakHandler;
public:
enum Type { BreakpointType, WatchpointType };
bool enabled; // Should we talk to the debugger engine?
bool pending; // Does the debugger engine know about us already?
Type type; // Type of breakpoint.
// This "user requested information" will get stored in the session.
QString fileName; // Short name of source file.
QByteArray condition; // Condition associated with breakpoint.
QByteArray ignoreCount; // Ignore count associated with breakpoint.
QByteArray lineNumber; // Line in source file.
QByteArray address; // Address for watchpoints.
QByteArray threadSpec; // Thread specification.
QString funcName; // Name of containing function.
bool useFullPath; // Should we use the full path when setting the bp?
// This is what gdb produced in response.
QByteArray bpNumber; // Breakpoint number assigned by the debugger engine.
QByteArray bpCondition; // Condition acknowledged by the debugger engine.
QByteArray bpIgnoreCount;// Ignore count acknowledged by the debugger engine.
QString bpFileName; // File name acknowledged by the debugger engine.
QString bpFullName; // Full file name acknowledged by the debugger engine.
QByteArray bpLineNumber; // Line number acknowledged by the debugger engine.
QByteArray bpCorrectedLineNumber; // Acknowledged by the debugger engine.
QByteArray bpThreadSpec; // Thread spec acknowledged by the debugger engine.
QString bpFuncName; // Function name acknowledged by the debugger engine.
QByteArray bpAddress; // Address acknowledged by the debugger engine.
bool bpMultiple; // Happens in constructors/gdb.
bool bpEnabled; // Enable/disable command sent.
void setMarkerFileName(const QString &fileName);
QString markerFileName() const { return m_markerFileName; }
void setMarkerLineNumber(int lineNumber);
int markerLineNumber() const { return m_markerLineNumber; }
private:
// Taken from either user input or gdb responses.
QString m_markerFileName; // Used to locate the marker.
int m_markerLineNumber;
// Our red blob in the editor.
BreakpointMarker *marker;
};
} // namespace Internal
} // namespace Debugger
#endif // DEBUGGER_BREAKPOINT_H
......@@ -355,29 +355,30 @@ void BreakWindow::editBreakpoint(const QModelIndexList &list)
ui.labelFileName->hide();
ui.lineEditLineNumber->hide();
ui.labelLineNumber->hide();
QAbstractItemModel *m = model();
//ui.lineEditFunction->setText(
// model()->data(idx.sibling(row, 1), role).toString());
// m->data(idx.sibling(row, 1), role).toString());
//ui.lineEditFileName->setText(
// model()->data(idx.sibling(row, 2), role).toString());
// m->data(idx.sibling(row, 2), role).toString());
//ui.lineEditLineNumber->setText(
// model()->data(idx.sibling(row, 3), role).toString());
// m->data(idx.sibling(row, 3), role).toString());
ui.lineEditCondition->setText(
model()->data(idx.sibling(row, 4), role).toString());
m->data(idx.sibling(row, 4), role).toString());
ui.lineEditIgnoreCount->setText(
model()->data(idx.sibling(row, 5), role).toString());
m->data(idx.sibling(row, 5), role).toString());
ui.lineEditThreadSpec->setText(
model()->data(idx.sibling(row, 6), role).toString());
m->data(idx.sibling(row, 6), role).toString());
if (dlg.exec() == QDialog::Rejected)
return;
foreach (const QModelIndex &idx, list) {
//model()->setData(idx.sibling(idx.row(), 1), ui.lineEditFunction->text());
//model()->setData(idx.sibling(idx.row(), 2), ui.lineEditFileName->text());
//model()->setData(idx.sibling(idx.row(), 3), ui.lineEditLineNumber->text());
model()->setData(idx.sibling(idx.row(), 4), ui.lineEditCondition->text());
model()->setData(idx.sibling(idx.row(), 5), ui.lineEditIgnoreCount->text());
model()->setData(idx.sibling(idx.row(), 6), ui.lineEditThreadSpec->text());
//m->setData(idx.sibling(idx.row(), 1), ui.lineEditFunction->text());
//m->setData(idx.sibling(idx.row(), 2), ui.lineEditFileName->text());
//m->setData(idx.sibling(idx.row(), 3), ui.lineEditLineNumber->text());
m->setData(idx.sibling(idx.row(), 4), ui.lineEditCondition->text());
m->setData(idx.sibling(idx.row(), 5), ui.lineEditIgnoreCount->text());
m->setData(idx.sibling(idx.row(), 6), ui.lineEditThreadSpec->text());
}
emit breakpointSynchronizationRequested();
}
......
......@@ -16,6 +16,7 @@ QT += gui \
script
HEADERS += breakhandler.h \
breakwindow.h \
breakpoint.h \
debuggeragents.h \
debuggeractions.h \
debuggerconstants.h \
......
......@@ -18,6 +18,7 @@
<file>images/debugger_stepoverproc_small.png</file>
<file>images/debugger_stop.png</file>
<file>images/debugger_stop_small.png</file>
<file>images/watchpoint.png</file>
<file>images/breakpoint_16.png</file>
<file>images/breakpoint_24.png</file>
<file>images/breakpoint_disabled_16.png</file>
......
......@@ -132,7 +132,8 @@ enum DebuggerCapabilities
BreakOnThrowAndCatchCapability = 0x200,
ReturnFromFunctionCapability = 0x400,
CreateFullBacktraceCapability = 0x800,
AddWatcherCapability = 0x1000
AddWatcherCapability = 0x1000,
WatchpointCapability = 0x2000
};
enum LogChannel
......
......@@ -897,9 +897,9 @@ BreakpointData *DebuggerManager::findBreakpoint(const QString &fileName, int lin
// FIXME: move further up the plugin where there's more specific context
// information available.
static BreakpointData *createBreakpointByFileAndLine
(const QString &fileName, int lineNumber, BreakHandler *handler)
(const QString &fileName, int lineNumber)
{
BreakpointData *data = new BreakpointData(handler);
BreakpointData *data = new BreakpointData;
if (lineNumber > 0) {
data->fileName = fileName;
data->lineNumber = QByteArray::number(lineNumber);
......@@ -934,7 +934,7 @@ void DebuggerManager::toggleBreakpoint(const QString &fileName, int lineNumber)
int index = d->m_breakHandler->findBreakpoint(fileName, lineNumber);
if (index == -1)
d->m_breakHandler->appendBreakpoint(
createBreakpointByFileAndLine(fileName, lineNumber, d->m_breakHandler));
createBreakpointByFileAndLine(fileName, lineNumber));
else
d->m_breakHandler->removeBreakpoint(index);
......@@ -1410,6 +1410,13 @@ void DebuggerManager::breakByFunction(const QString &functionName)
attemptBreakpointSynchronization();
}
void DebuggerManager::appendBreakpoint(BreakpointData *data)
{
QTC_ASSERT(d->m_breakHandler, return);
d->m_breakHandler->appendBreakpoint(data);
attemptBreakpointSynchronization();
}
void DebuggerManager::setBusyCursor(bool busy)
{
//STATE_DEBUG("BUSY FROM: " << d->m_busy << " TO: " << d->m_busy);
......
......@@ -289,6 +289,7 @@ public slots: // FIXME
void reloadRegisters();
void registerDockToggled(bool on);
void clearStatusMessage();
void appendBreakpoint(Internal::BreakpointData *data);
void attemptBreakpointSynchronization();
void reloadFullStack();
void operateByInstructionTriggered();
......
......@@ -1394,7 +1394,19 @@ void GdbEngine::handleStop1(const GdbMi &data)
reloadBreakListInternal();
}
if (reason == "breakpoint-hit") {
if (reason == "watchpoint-trigger") {
// *stopped,reason="watchpoint-trigger",wpt={number="2",exp="*0xbfffed40"},
// value={old="1",new="0"},frame={addr="0x00451e1b",
// func="QScopedPointer",args=[{name="this",value="0xbfffed40"},
// {name="p",value="0x0"}],file="x.h",fullname="/home/.../x.h",line="95"},
// thread-id="1",stopped-threads="all",core="2"
GdbMi wpt = data.findChild("wpt");
QByteArray bpNumber = wpt.findChild("number").data();
QByteArray bpAddress = wpt.findChild("exp").data();
//QByteArray threadId = data.findChild("thread-id").data();
showStatusMessage(tr("Watchpoint %1 at %2 triggered:")
.arg(_(bpNumber), _(bpAddress)));
} else if (reason == "breakpoint-hit") {
QByteArray bpNumber = data.findChild("bkptno").data();
QByteArray threadId = data.findChild("thread-id").data();
showStatusMessage(tr("Stopped at breakpoint %1 in thread %2")
......@@ -1825,6 +1837,7 @@ unsigned GdbEngine::debuggerCapabilities() const
| ReloadModuleSymbolsCapability | BreakOnThrowAndCatchCapability
| ReturnFromFunctionCapability
| CreateFullBacktraceCapability
| WatchpointCapability
| AddWatcherCapability;
}
......@@ -2174,6 +2187,13 @@ void GdbEngine::sendInsertBreakpoint(int index)
const BreakpointData *data = manager()->breakHandler()->at(index);