Commit 0afb34d5 authored by hjk's avatar hjk
Browse files

debugger: refactor breakpoint type, add function name to resolved jsbreakpoints

parent 7c36f472
......@@ -90,7 +90,34 @@ QDataStream &operator<<(QDataStream &s, const JSAgentStackData &data)
return s << data.functionName << data.fileName << data.lineNumber;
}
typedef JSAgentStackData JSBreakpointData;
struct JSAgentBreakpointData
{
QByteArray functionName;
QByteArray fileName;
qint32 lineNumber;
};
typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints;
QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data)
{
return s << data.functionName << data.fileName << data.lineNumber;
}
QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data)
{
return s >> data.functionName >> data.fileName >> data.lineNumber;
}
bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2)
{
return b1.lineNumber == b2.lineNumber && b1.fileName == b2.fileName;
}
uint qHash(const JSAgentBreakpointData &b)
{
return b.lineNumber ^ qHash(b.fileName);
}
class JSDebuggerAgentPrivate
{
......@@ -115,8 +142,8 @@ public:
int stepCount;
QEventLoop loop;
QHash <qint64, QString> filenames;
QSet< QPair<QString, qint32> > breakpointList;
QHash<qint64, QString> filenames;
JSAgentBreakpoints breakpoints;
QStringList watchExpressions;
QSet<qint64> knownObjectIds;
};
......@@ -348,7 +375,7 @@ void JSDebuggerAgentPrivate::positionChange
return; //no re-entrency
// check breakpoints
if (!breakpointList.isEmpty()) {
if (!breakpoints.isEmpty()) {
QHash<qint64, QString>::const_iterator it = filenames.constFind(scriptId);
QScriptContext *ctx = engine()->currentContext();
QScriptContextInfo info(ctx);
......@@ -362,8 +389,10 @@ void JSDebuggerAgentPrivate::positionChange
QPair<QString, qint32> key = qMakePair(filename, lineNumber);
it = filenames.insert(scriptId, filename);
}
QPair<QString, qint32> key = qMakePair(*it, lineNumber);
if (breakpointList.contains(key)) {
JSAgentBreakpointData bp;
bp.fileName = it->toUtf8();
bp.lineNumber = lineNumber;
if (breakpoints.contains(bp)) {
stopped();
return;
}
......@@ -440,7 +469,10 @@ void JSDebuggerAgentPrivate::messageReceived(const QByteArray &message)
QByteArray command;
ds >> command;
if (command == "BREAKPOINTS") {
ds >> breakpointList;
ds >> breakpoints;
//qDebug() << "BREAKPOINTS";
//foreach (const JSAgentBreakpointData &bp, breakpoints)
// qDebug() << "BREAKPOINT: " << bp.fileName << bp.lineNumber;
} else if (command == "WATCH_EXPRESSIONS") {
ds >> watchExpressions;
} else if (command == "STEPOVER") {
......
......@@ -148,7 +148,7 @@ int BreakHandler::findWatchPointIndexByAddress(quint64 address) const
{
for (int index = size() - 1; index >= 0; --index) {
BreakpointData *bd = at(index);
if (bd->type == BreakpointData::WatchpointType && bd->address == address)
if (bd->isWatchpoint() && bd->address == address)
return index;
}
return -1;
......@@ -168,9 +168,9 @@ void BreakHandler::saveBreakpoints()
const BreakpointData *data = at(index);
QMap<QString, QVariant> map;
// Do not persist Watchpoints.
//if (data->type == BreakpointData::WatchpointType)
//if (data->isWatchpoint())
// continue;
if (data->type != BreakpointData::BreakpointType)
if (data->type != BreakpointByFileAndLine)
map.insert(_("type"), data->type);
if (!data->fileName.isEmpty())
map.insert(_("filename"), data->fileName);
......@@ -235,7 +235,7 @@ void BreakHandler::loadBreakpoints()
data->useFullPath = bool(v.toInt());
v = map.value(_("type"));
if (v.isValid())
data->type = BreakpointData::Type(v.toInt());
data->type = BreakpointType(v.toInt());
data->setMarkerFileName(data->fileName);
data->setMarkerLineNumber(data->lineNumber);
append(data);
......@@ -344,7 +344,7 @@ QVariant BreakHandler::data(const QModelIndex &mi, int role) const
return str.isEmpty() ? empty : str;
}
if (role == Qt::DecorationRole) {
if (data->type == BreakpointData::WatchpointType)
if (data->isWatchpoint())
return m_watchpointIcon;
if (!data->enabled)
return m_disabledBreakpointIcon;
......@@ -414,8 +414,8 @@ QVariant BreakHandler::data(const QModelIndex &mi, int role) const
case 7:
if (role == Qt::DisplayRole) {
QString displayValue;
const quint64 effectiveAddress = data->type == BreakpointData::WatchpointType ?
data->address : data->bpAddress;
const quint64 effectiveAddress
= data->isWatchpoint() ? data->address : data->bpAddress;
if (effectiveAddress)
displayValue += QString::fromAscii("0x%1").arg(effectiveAddress, 0, 16);
if (!data->bpState.isEmpty()) {
......
......@@ -61,7 +61,7 @@ static quint64 nextBPId() {
BreakpointData::BreakpointData() :
id(nextBPId()), enabled(true),
pending(true), type(BreakpointType),
pending(true), type(BreakpointByFileAndLine),
ignoreCount(0), lineNumber(0), address(0),
useFullPath(false),
bpIgnoreCount(0), bpLineNumber(0),
......@@ -84,7 +84,7 @@ BreakpointData *BreakpointData::clone() const
data->threadSpec = threadSpec;
data->funcName = funcName;
data->useFullPath = useFullPath;
if (isSetByFunction()) {
if (data->type == BreakpointByFunction) {
// FIXME: Would removing it be better then leaving this
// "history" around?
data->m_markerFileName = m_markerFileName;
......@@ -142,6 +142,22 @@ static inline void formatAddress(QTextStream &str, quint64 address)
QString BreakpointData::toToolTip() const
{
QString t = tr("Unknown Breakpoint Type");
switch (type) {
case BreakpointByFileAndLine:
t = tr("Breakpoint by File and Line");
break;
case BreakpointByFunction:
t = tr("Breakpoint by Function");
break;
case BreakpointByAddress:
t = tr("Breakpoint by Address");
break;
case Watchpoint:
t = tr("Watchpoint");
break;
}
QString rc;
QTextStream str(&rc);
str << "<html><body><table>"
......@@ -152,11 +168,7 @@ QString BreakpointData::toToolTip() const
<< "<tr><td>" << tr("Breakpoint Number:")
<< "</td><td>" << bpNumber << "</td></tr>"
<< "<tr><td>" << tr("Breakpoint Type:")
<< "</td><td>"
<< (type == BreakpointType ? tr("Breakpoint")
: type == WatchpointType ? tr("Watchpoint")
: tr("Unknown breakpoint type"))
<< "</td></tr>"
<< "</td><td>" << t << "</td></tr>"
<< "<tr><td>" << tr("State:")
<< "</td><td>" << bpState << "</td></tr>"
<< "</table><br><hr><table>"
......@@ -166,7 +178,8 @@ QString BreakpointData::toToolTip() const
<< "<tr><td>" << tr("Internal Number:")
<< "</td><td>&mdash;</td><td>" << bpNumber << "</td></tr>"
<< "<tr><td>" << tr("File Name:")
<< "</td><td>" << QDir::toNativeSeparators(fileName) << "</td><td>" << QDir::toNativeSeparators(bpFileName) << "</td></tr>"
<< "</td><td>" << QDir::toNativeSeparators(fileName)
<< "</td><td>" << QDir::toNativeSeparators(bpFileName) << "</td></tr>"
<< "<tr><td>" << tr("Function Name:")
<< "</td><td>" << funcName << "</td><td>" << bpFuncName << "</td></tr>"
<< "<tr><td>" << tr("Line Number:") << "</td><td>";
......
......@@ -47,6 +47,14 @@ class BreakHandler;
//
//////////////////////////////////////////////////////////////////
enum BreakpointType
{
BreakpointByFileAndLine,
BreakpointByFunction,
BreakpointByAddress,
Watchpoint,
};
class BreakpointData
{
public:
......@@ -77,13 +85,11 @@ private:
friend class BreakHandler;
public:
enum Type { BreakpointType, WatchpointType };
quint64 id;
bool enabled; // Should we talk to the debugger engine?
bool pending; // Does the debugger engine know about us already?
Type type; // Type of breakpoint.
BreakpointType type; // Type of breakpoint.
// This "user requested information" will get stored in the session.
QString fileName; // Short name of source file.
......@@ -118,12 +124,13 @@ public:
void setMarkerLineNumber(int lineNumber);
int markerLineNumber() const { return m_markerLineNumber; }
bool isSetByFunction() const { return !funcName.isEmpty(); }
bool isSetByFileAndLine() const { return !fileName.isEmpty(); }
bool isWatchpoint() const { return type == Watchpoint; }
bool isBreakpoint() const { return type != Watchpoint; } // Enough for now.
Q_DECLARE_TR_FUNCTIONS(BreakHandler)
// TODO: move those to breakhandler
private:
// Taken from either user input or gdb responses.
QString m_markerFileName; // Used to locate the marker.
int m_markerLineNumber;
......
......@@ -81,7 +81,8 @@ BreakpointDialog::BreakpointDialog(QWidget *parent) : QDialog(parent)
comboBoxType->insertItem(3, tr("Address"));
pathChooserFileName->setExpectedKind(Utils::PathChooser::File);
connect(comboBoxType, SIGNAL(activated(int)), SLOT(typeChanged(int)));
lineEditIgnoreCount->setValidator(new QIntValidator(0, 2147483647, lineEditIgnoreCount));
lineEditIgnoreCount->setValidator(
new QIntValidator(0, 2147483647, lineEditIgnoreCount));
}
bool BreakpointDialog::showDialog(BreakpointData *data)
......
......@@ -38,11 +38,12 @@ namespace Internal {
enum { debugBP = 0 };
// Convert breakpoint structs
CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd)
CdbCore::BreakPoint breakPointFromBreakPointData(const BreakpointData &bpd)
{
CdbCore::BreakPoint rc;
rc.type = bpd.type == Debugger::Internal::BreakpointData::BreakpointType ?
CdbCore::BreakPoint::Code : CdbCore::BreakPoint::Data;
rc.type = bpd.type == Watchpoint ?
CdbCore::BreakPoint::Data :
CdbCore::BreakPoint::Code ;
rc.address = bpd.address;
if (!bpd.threadSpec.isEmpty()) {
......
......@@ -2114,10 +2114,11 @@ void GdbEngine::setBreakpointDataFromOutput(BreakpointData *data, const GdbMi &b
} else if (child.hasName("thread")) {
data->bpThreadSpec = child.data();
} else if (child.hasName("type")) {
if (child.data().contains("reakpoint")) // "breakpoint", "hw breakpoint"
data->type = BreakpointData::BreakpointType;
else // FIXME: Incomplete list of cases.
data->type = BreakpointData::WatchpointType;
// FIXME: This should not change the type.
//if (child.data().contains("reakpoint")) // "breakpoint", "hw breakpoint"
// ; // data->type = BreakpointData::BreakpointType;
//else // FIXME: Incomplete list of cases.
// data->type = Watchpoint;
}
// This field is not present. Contents needs to be parsed from
// the plain "ignore" response.
......@@ -2179,7 +2180,7 @@ void GdbEngine::sendInsertBreakpoint(int index)
const BreakpointData *data = breakHandler()->at(index);
// Set up fallback in case of pending breakpoints which aren't handled
// by the MI interface.
if (data->type == BreakpointData::WatchpointType) {
if (data->type == Watchpoint) {
postCommand("watch " + bpAddressSpec(data->address),
NeedsStop | RebuildBreakpointModel,
CB(handleWatchInsert), index);
......
......@@ -84,6 +84,36 @@ enum {
namespace Debugger {
namespace Internal {
struct JSAgentBreakpointData
{
QByteArray functionName;
QByteArray fileName;
qint32 lineNumber;
};
uint qHash(const JSAgentBreakpointData &b)
{
return b.lineNumber ^ qHash(b.fileName);
}
QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data)
{
return s << data.functionName << data.fileName << data.lineNumber;
}
QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data)
{
return s >> data.functionName >> data.fileName >> data.lineNumber;
}
bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2)
{
return b1.lineNumber == b2.lineNumber && b1.fileName == b2.fileName;
}
typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints;
QDataStream &operator>>(QDataStream &s, WatchData &data)
{
data = WatchData();
......@@ -466,27 +496,31 @@ void QmlEngine::attemptBreakpointSynchronization()
{
Internal::BreakHandler *handler = breakHandler();
//bool updateNeeded = false;
QSet< QPair<QString, qint32> > breakList;
Internal::JSAgentBreakpoints breakpoints;
for (int index = 0; index != handler->size(); ++index) {
Internal::BreakpointData *data = handler->at(index);
QString processedFilename = data->fileName;
if (isShadowBuildProject())
processedFilename = toShadowBuildFilename(data->fileName);
breakList << qMakePair(processedFilename, data->lineNumber);
Internal::JSAgentBreakpointData bp;
bp.fileName = processedFilename.toUtf8();
bp.lineNumber = data->lineNumber;
bp.functionName = data->funcName.toUtf8();
breakpoints.insert(bp);
}
QByteArray reply;
QDataStream rs(&reply, QIODevice::WriteOnly);
rs << QByteArray("BREAKPOINTS");
rs << breakList;
//qDebug() << Q_FUNC_INFO << breakList;
rs << breakpoints;
//qDebug() << Q_FUNC_INFO << breakpoints;
sendMessage(reply);
}
bool QmlEngine::acceptsBreakpoint(const Internal::BreakpointData *br)
{
return br->fileName.endsWith(QLatin1String("qml"))
|| br->fileName.endsWith(QLatin1String("js"));
return br->fileName.endsWith(QLatin1String(".qml"))
|| br->fileName.endsWith(QLatin1String(".js"));
}
void QmlEngine::loadSymbols(const QString &moduleName)
......@@ -624,11 +658,12 @@ void QmlEngine::messageReceived(const QByteArray &message)
QByteArray command;
stream >> command;
showMessage(QLatin1String("RECEIVED RESPONSE: ") + Internal::quoteUnprintableLatin1(message));
showMessage(QLatin1String("RECEIVED RESPONSE: ")
+ Internal::quoteUnprintableLatin1(message));
if (command == "STOPPED") {
if (state() == InferiorRunOk) {
if (state() == InferiorRunOk)
notifyInferiorSpontaneousStop();
}
Internal::StackFrames stackFrames;
QList<Internal::WatchData> watches;
......@@ -684,11 +719,13 @@ void QmlEngine::messageReceived(const QByteArray &message)
// Make breakpoint non-pending
//
QString file;
QString function;
int line = -1;
if (!stackFrames.isEmpty()) {
file = stackFrames.at(0).file;
line = stackFrames.at(0).line;
function = stackFrames.at(0).function;
if (isShadowBuildProject()) {
file = fromShadowBuildFilename(file);
......@@ -700,11 +737,11 @@ void QmlEngine::messageReceived(const QByteArray &message)
Internal::BreakpointData *data = handler->at(index);
QString processedFilename = data->fileName;
if (processedFilename == file
&& data->lineNumber == line) {
if (processedFilename == file && data->lineNumber == line) {
data->pending = false;
data->bpFileName = file;
data->bpLineNumber = line;
data->bpFuncName = function;
handler->updateMarker(data);
}
}
......
......@@ -835,7 +835,7 @@ bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int ro
const int index = handler->findWatchPointIndexByAddress(address);
if (index == -1) {
BreakpointData *data = new BreakpointData;
data->type = BreakpointData::WatchpointType;
data->type = Watchpoint;
data->address = address;
handler->appendBreakpoint(data);
} else {
......
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