Commit 2cd7e615 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Debugger[CDB]: Add WatchPoints and breakpoint thread restriction.

parent f678fce3
......@@ -50,48 +50,21 @@ namespace CdbCore {
static const char sourceFileQuoteC = '`';
BreakPoint::BreakPoint() :
type(Code),
lineNumber(-1),
address(0),
threadId(-1),
ignoreCount(0),
oneShot(false),
enabled(true)
{
}
int BreakPoint::compare(const BreakPoint& rhs) const
{
if (lineNumber > rhs.lineNumber)
return 1;
if (lineNumber < rhs.lineNumber)
return -1;
if (address > rhs.address)
return 1;
if (address < rhs.address)
return -1;
if (ignoreCount > rhs.ignoreCount)
return 1;
if (ignoreCount < rhs.ignoreCount)
return -1;
if (oneShot && !rhs.oneShot)
return 1;
if (!oneShot && rhs.oneShot)
return -1;
if (enabled && !rhs.enabled)
return 1;
if (!enabled && rhs.enabled)
return -1;
if (const int fileCmp = fileName.compare(rhs.fileName))
return fileCmp;
if (const int funcCmp = funcName.compare(rhs.funcName))
return funcCmp;
if (const int condCmp = condition.compare(rhs.condition))
return condCmp;
return 0;
}
void BreakPoint::clear()
{
type = Code;
ignoreCount = 0;
threadId = -1;
oneShot = false;
enabled = true;
clearExpressionData();
......@@ -108,23 +81,35 @@ void BreakPoint::clearExpressionData()
QDebug operator<<(QDebug dbg, const BreakPoint &bp)
{
QDebug nsp = dbg.nospace();
if (bp.address)
nsp << "0x" << QString::number(bp.address, 16) << ' ';
if (!bp.fileName.isEmpty()) {
nsp << "fileName='" << bp.fileName << ':' << bp.lineNumber << '\'';
dbg.nospace() << bp.toString();
return dbg;
}
QString BreakPoint::toString() const
{
QString rc;
QTextStream str(&rc);
str << (type == BreakPoint::Code ? "Code " : "Data ");
if (address) {
str.setIntegerBase(16);
str << "0x" << address << ' ';
str.setIntegerBase(10);
}
if (!fileName.isEmpty()) {
str << "fileName='" << fileName << ':' << lineNumber << '\'';
} else {
nsp << "funcName='" << bp.funcName << '\'';
str << "funcName='" << funcName << '\'';
}
if (!bp.condition.isEmpty())
nsp << " condition='" << bp.condition << '\'';
if (bp.ignoreCount)
nsp << " ignoreCount=" << bp.ignoreCount;
if (bp.enabled)
nsp << " enabled";
if (bp.oneShot)
nsp << " oneShot";
return dbg;
if (threadId >= 0)
str << " thread=" << threadId;
if (!condition.isEmpty())
str << " condition='" << condition << '\'';
if (ignoreCount)
str << " ignoreCount=" << ignoreCount;
str << (enabled ? " enabled" : " disabled");
if (oneShot)
str << " oneShot";
return rc;
}
QString BreakPoint::expression() const
......@@ -155,25 +140,59 @@ QString BreakPoint::expression() const
return rc;
}
static inline QString msgCannotSetBreakpoint(const QString &exp, const QString &why)
{
return QString::fromLatin1("Unable to set breakpoint '%1' : %2").arg(exp, why);
}
bool BreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const
{
const QString expr = expression();
if (debugBP)
qDebug() << Q_FUNC_INFO << *this << expr;
const HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast<PCWSTR>(expr.utf16()));
HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast<PCWSTR>(expr.utf16()));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to set breakpoint '%1' : %2").
arg(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr));
*errorMessage = msgCannotSetBreakpoint(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr));
return false;
}
// Pass Count is ignoreCount + 1
ibp->SetPassCount(ignoreCount + 1u);
hr = ibp->SetPassCount(ignoreCount + 1u);
if (FAILED(hr))
qWarning("Error setting passcount %d %s ", ignoreCount, qPrintable(expr));
// Set up size for data breakpoints
if (type == Data) {
const ULONG size = 1u;
hr = ibp->SetDataParameters(size, DEBUG_BREAK_READ | DEBUG_BREAK_WRITE);
if (FAILED(hr)) {
const QString msg = QString::fromLatin1("Cannot set watch size to %1: %2").
arg(size).arg(CdbCore::msgComFailed("SetDataParameters", hr));
*errorMessage = msgCannotSetBreakpoint(expr, msg);
return false;
}
}
// Thread
if (threadId >= 0) {
hr = ibp->SetMatchThreadId(threadId);
if (FAILED(hr)) {
const QString msg = QString::fromLatin1("Cannot set thread id to %1: %2").
arg(threadId).arg(CdbCore::msgComFailed("SetMatchThreadId", hr));
*errorMessage = msgCannotSetBreakpoint(expr, msg);
return false;
}
}
// Flags
ULONG flags = 0;
if (enabled)
flags |= DEBUG_BREAKPOINT_ENABLED;
if (oneShot)
flags |= DEBUG_BREAKPOINT_ONE_SHOT;
ibp->AddFlags(flags);
hr = ibp->AddFlags(flags);
if (FAILED(hr)) {
const QString msg = QString::fromLatin1("Cannot set flags to 0x%1: %2").
arg(flags, 0 ,16).arg(CdbCore::msgComFailed("AddFlags", hr));
*errorMessage = msgCannotSetBreakpoint(expr, msg);
return false;
}
return true;
}
......@@ -192,7 +211,8 @@ bool BreakPoint::add(CIDebugControl* debugControl,
*address = 0;
if (id)
*id = 0;
HRESULT hr = debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &ibp);
const ULONG iType = type == Code ? DEBUG_BREAKPOINT_CODE : DEBUG_BREAKPOINT_DATA;
HRESULT hr = debugControl->AddBreakpoint2(iType, DEBUG_ANY_ID, &ibp);
if (FAILED(hr)) {
*errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("AddBreakpoint2", hr));
return false;
......@@ -321,16 +341,41 @@ void BreakPoint::clearNormalizeFileNameCache()
normalizedFileNameCache()->clear();
}
static inline QString msgCannotRetrieveBreakpoint(const QString &why)
{
return QString::fromLatin1("Cannot retrieve breakpoint: %1").arg(why);
}
static inline int threadIdOfBreakpoint(CIDebugBreakpoint *ibp)
{
// Thread: E_NOINTERFACE indicates no thread has been set.
int threadId = -1;
ULONG iThreadId;
if (S_OK == ibp->GetMatchThreadId(&iThreadId))
threadId = iThreadId;
return threadId;
}
bool BreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
{
clear();
// Get type
ULONG iType;
ULONG processorType;
HRESULT hr = ibp->GetType(&iType, &processorType);
if (FAILED(hr)) {
*errorMessage = msgCannotRetrieveBreakpoint(CdbCore::msgComFailed("GetType", hr));
return false;
}
type = iType == DEBUG_BREAKPOINT_CODE ? Code : Data;
// Get & parse expression
WCHAR wszBuf[MAX_PATH];
const HRESULT hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0);
hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint: %1").
arg(CdbCore::msgComFailed("GetOffsetExpressionWide", hr));
*errorMessage = msgCannotRetrieveBreakpoint(CdbCore::msgComFailed("GetOffsetExpressionWide", hr));
return false;
}
threadId = threadIdOfBreakpoint(ibp);
// Pass Count is ignoreCount + 1
ibp->GetPassCount(&ignoreCount);
if (ignoreCount)
......@@ -523,4 +568,34 @@ bool BreakPoint::setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id,
return true;
}
// Change thread-id of a breakpoint
static inline QString msgCannotSetBreakPointThread(unsigned long id, int tid, const QString &why)
{
return QString::fromLatin1("Cannot set breakpoint %1 thread to %2: %3").arg(id).arg(tid).arg(why);
}
bool BreakPoint::setBreakPointThreadById(CIDebugControl *ctl, unsigned long id, int threadId, QString *errorMessage)
{
if (debugBP)
qDebug() << Q_FUNC_INFO << id << threadId;
CIDebugBreakpoint *ibp = breakPointById(ctl, id, errorMessage);
if (!ibp) {
*errorMessage = msgCannotSetBreakPointThread(id, threadId, *errorMessage);
return false;
}
// Compare thread ids
const int oldThreadId = threadIdOfBreakpoint(ibp);
if (oldThreadId == threadId)
return true;
const ULONG newIThreadId = threadId == -1 ? DEBUG_ANY_ID : static_cast<ULONG>(threadId);
if (debugBP)
qDebug() << "Changing thread id of " << id << " from " << oldThreadId << " to " << threadId
<< '(' << newIThreadId << ')';
const HRESULT hr = ibp->SetMatchThreadId(newIThreadId);
if (FAILED(hr)) {
*errorMessage = msgCannotSetBreakPointThread(id, threadId, *errorMessage);
return false;
}
return true;
}
} // namespace CdbCore
......@@ -49,18 +49,21 @@ namespace CdbCore {
struct BreakPoint
{
BreakPoint();
enum Type { Code, // Stop in code.
Data // Stop when accessing address.
};
int compare(const BreakPoint& rhs) const;
BreakPoint();
void clear();
void clearExpressionData();
QString expression() const;
// Apply parameters
// Apply parameters (with the exception of type, which is
// passed as a parameter to IDebugControl within add().
bool apply(IDebugBreakpoint2 *ibp, QString *errorMessage) const;
// Convenience to add to a IDebugControl4
// Convenience to add to a IDebugControl4.
bool add(CIDebugControl* debugControl,
QString *errorMessage,
unsigned long *id = 0,
......@@ -76,16 +79,20 @@ struct BreakPoint
static IDebugBreakpoint2 *breakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage);
static bool removeBreakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage);
static bool setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool enabled, QString *errorMessage);
static bool setBreakPointThreadById(CIDebugControl *ctl, unsigned long id, int threadId, QString *errorMessage);
// Return a 'canonical' file (using '/' and capitalized drive letter)
static QString normalizeFileName(const QString &f);
static void clearNormalizeFileNameCache();
QString toString() const;
Type type;
QString fileName; // short name of source file
int lineNumber; // line in source file
QString funcName; // name of containing function
quint64 address;
int threadId;
QString condition; // condition associated with breakpoint
unsigned long ignoreCount; // ignore count associated with breakpoint
bool oneShot;
......@@ -94,10 +101,6 @@ struct BreakPoint
QDebug operator<<(QDebug, const BreakPoint &bp);
inline bool operator==(const BreakPoint& b1, const BreakPoint& b2)
{ return b1.compare(b2) == 0; }
inline bool operator!=(const BreakPoint& b1, const BreakPoint& b2)
{ return b1.compare(b2) != 0; }
}
} // namespace CdbCore
#endif // CDBCOREBREAKPOINTS_H
......@@ -37,6 +37,37 @@ namespace Internal {
enum { debugBP = 0 };
// Convert breakpoint structs
CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd)
{
CdbCore::BreakPoint rc;
rc.type = bpd.type == Debugger::Internal::BreakpointData::BreakpointType ?
CdbCore::BreakPoint::Code : CdbCore::BreakPoint::Data;
if (rc.type == CdbCore::BreakPoint::Data) {
QByteArray addressBA = bpd.address;
if (addressBA.startsWith("0x"))
addressBA.remove(0, 2);
bool ok;
rc.address = addressBA.toULongLong(&ok, 16);
if (!ok)
qWarning("Cdb: Cannot convert watchpoint address '%s'", bpd.address.constData());
}
if (!bpd.threadSpec.isEmpty()) {
bool ok;
rc.threadId = bpd.threadSpec.toInt(&ok);
if (!ok)
qWarning("Cdb: Cannot convert breakpoint thread specification '%s'", bpd.address.constData());
}
rc.fileName = QDir::toNativeSeparators(bpd.fileName);
rc.condition = bpd.condition;
rc.funcName = bpd.funcName;
rc.ignoreCount = bpd.ignoreCount.isEmpty() ? 0 : bpd.ignoreCount.toInt();
rc.lineNumber = bpd.lineNumber.isEmpty() ? -1 : bpd.lineNumber.toInt();
rc.oneShot = false;
rc.enabled = bpd.enabled;
return rc;
}
static inline QString msgCannotSetBreakAtFunction(const QString &func, const QString &why)
{
......@@ -93,7 +124,7 @@ bool synchronizeBreakPoints(CIDebugControl* debugControl,
breakPointOk = ncdbbp.add(debugControl, &warning, &id, &address);
if (breakPointOk) {
if (debugBP)
qDebug() << "Added " << id << " at " << address << ncdbbp;
qDebug("Added %lu at 0x%lx %s", id, address, qPrintable(ncdbbp.toString()));
handler->takeInsertedBreakPoint(nbd);
updateMarkers = true;
nbd->pending = false;
......@@ -123,6 +154,18 @@ bool synchronizeBreakPoints(CIDebugControl* debugControl,
foreach (BreakpointData *dbd, handler->takeDisabledBreakpoints())
if (!CdbCore::BreakPoint::setBreakPointEnabledById(debugControl, dbd->bpNumber.toUInt(), false, &warning))
warnings->push_back(warning);
// Check for modified thread ids.
for (int i = handler->size() - 1; i >= 0; i--) {
BreakpointData *bpd = handler->at(i);
if (bpd->threadSpec != bpd->bpThreadSpec) {
const int newThreadSpec = bpd->threadSpec.isEmpty() ? -1 : bpd->threadSpec.toInt();
if (CdbCore::BreakPoint::setBreakPointThreadById(debugControl, bpd->bpNumber.toUInt(), newThreadSpec, errorMessage)) {
bpd->bpThreadSpec = bpd->threadSpec;
} else {
qWarning("%s", qPrintable(*errorMessage));
}
}
}
if (updateMarkers)
handler->updateMarkers();
......@@ -130,7 +173,11 @@ bool synchronizeBreakPoints(CIDebugControl* debugControl,
if (debugBP > 1) {
QList<CdbCore::BreakPoint> bps;
CdbCore::BreakPoint::getBreakPoints(debugControl, &bps, errorMessage);
qDebug().nospace() << "### Breakpoints in engine: " << bps;
QDebug nsp = qDebug().nospace();
const int count = bps.size();
nsp <<"### Breakpoints in engine: " << count << '\n';
for (int i = 0; i < count; i++)
nsp << " #" << i << ' ' << bps.at(i) << '\n';
}
return true;
}
......
......@@ -42,23 +42,12 @@ QT_BEGIN_NAMESPACE
class QDebug;
QT_END_NAMESPACE
// Convert breakpoint structs
inline CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd)
{
CdbCore::BreakPoint rc;
rc.fileName = QDir::toNativeSeparators(bpd.fileName);
rc.condition = bpd.condition;
rc.funcName = bpd.funcName;
rc.ignoreCount = bpd.ignoreCount.isEmpty() ? 0 : bpd.ignoreCount.toInt();
rc.lineNumber = bpd.lineNumber.isEmpty() ? -1 : bpd.lineNumber.toInt();
rc.oneShot = false;
rc.enabled = bpd.enabled;
return rc;
}
namespace Debugger {
namespace Internal {
// Convert breakpoint structs
CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd);
// Synchronize (halted) engine with BreakHandler.
bool synchronizeBreakPoints(CIDebugControl* ctl, CIDebugSymbols *syms,
BreakHandler *bh,
......
......@@ -1539,7 +1539,9 @@ void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP)
expression.clear();
}
if (!expression.isEmpty())
m_stoppedMessage = CdbDebugEngine::tr("Breakpoint: %1").arg(expression);
m_stoppedMessage = breakpoint.type == CdbCore::BreakPoint::Code ?
CdbDebugEngine::tr("Breakpoint: %1").arg(expression) :
CdbDebugEngine::tr("Watchpoint: %1").arg(expression);
}
void CdbDebugEngine::reloadSourceFiles()
......@@ -1560,7 +1562,8 @@ void CdbDebugEngine::syncDebuggerPaths()
unsigned CdbDebugEngine::debuggerCapabilities() const
{
return DisassemblerCapability | RegisterCapability | ShowMemoryCapability;
return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
|WatchpointCapability;
}
// Accessed by DebuggerManager
......
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