"README.md" did not exist on "a71fcf633464300b39b1caa30f3534f90b5016fa"
Newer
Older
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 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://www.qtsoftware.com/contact.
**
**************************************************************************/
#include "cdbbreakpoint.h"
#include "breakhandler.h"
#include "cdbdebugengine_p.h"
#include <QtCore/QTextStream>
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QMap>
namespace Debugger {
namespace Internal {
// The CDB breakpoint expression syntax is:
// `foo.cpp:523`[ "condition"]
// module!function[ "condition"]
static const char sourceFileQuoteC = '`';
CDBBreakPoint::CDBBreakPoint() :
ignoreCount(0),
{
}
CDBBreakPoint::CDBBreakPoint(const BreakpointData &bpd) :
fileName(QDir::toNativeSeparators(bpd.fileName)),
condition(bpd.condition),
ignoreCount(0),
funcName(bpd.funcName),
{
if (!bpd.ignoreCount.isEmpty())
ignoreCount = bpd.ignoreCount.toInt();
if (!bpd.lineNumber.isEmpty())
lineNumber = bpd.lineNumber.toInt();
}
int CDBBreakPoint::compare(const CDBBreakPoint& rhs) const
{
if (ignoreCount > rhs.ignoreCount)
return 1;
if (ignoreCount < rhs.ignoreCount)
return -1;
if (lineNumber > rhs.lineNumber)
return 1;
if (lineNumber < rhs.lineNumber)
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 CDBBreakPoint::clear()
{
ignoreCount = 0;
}
void CDBBreakPoint::clearExpressionData()
{
fileName.clear();
condition.clear();
funcName.clear();
lineNumber = -1;
}
QDebug operator<<(QDebug dbg, const CDBBreakPoint &bp)
{
QDebug nsp = dbg.nospace();
if (!bp.fileName.isEmpty()) {
nsp << "fileName='" << bp.fileName << ':' << bp.lineNumber << '\'';
} else {
nsp << "funcName='" << bp.funcName << '\'';
}
if (!bp.condition.isEmpty())
nsp << " condition='" << bp.condition << '\'';
if (bp.ignoreCount)
nsp << " ignoreCount=" << bp.ignoreCount;
if (bp.oneShot)
nsp << " oneShot";
return dbg;
}
QString CDBBreakPoint::expression() const
{
// format the breakpoint expression (file/function and condition)
QString rc;
QTextStream str(&rc);
if (funcName.isEmpty()) {
const QChar sourceFileQuote = QLatin1Char(sourceFileQuoteC);
str << sourceFileQuote << QDir::toNativeSeparators(fileName) << QLatin1Char(':') << lineNumber << sourceFileQuote;
} else {
str << funcName;
}
if (!condition.isEmpty()) {
const QChar doubleQuote = QLatin1Char('"');
str << QLatin1Char(' ') << doubleQuote << condition << doubleQuote;
}
return rc;
}
bool CDBBreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const
{
const QString expr = expression();
if (debugCDB)
qDebug() << Q_FUNC_INFO << *this << expr;

axasia
committed
const HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast<PCWSTR>(expr.utf16()));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to set breakpoint '%1' : %2").
arg(expr, msgComFailed("SetOffsetExpressionWide", hr));
return false;
}
// Pass Count is ignoreCount + 1
ibp->SetPassCount(ignoreCount + 1u);
ULONG flags = 0;
if (enabled)
flags |= DEBUG_BREAKPOINT_ENABLED;
if (oneShot)
flags |= DEBUG_BREAKPOINT_ONE_SHOT;
ibp->AddFlags(flags);
static inline QString msgCannotAddBreakPoint(const QString &why)
{
return QString::fromLatin1("Unable to add breakpoint: %1").arg(why);
}
bool CDBBreakPoint::add(CIDebugControl* debugControl,
quint64 *address, unsigned long *id,
QString *errorMessage) const
*address = 0;
*id = 0;
HRESULT hr = debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &ibp);
*errorMessage = msgCannotAddBreakPoint(msgComFailed("AddBreakpoint2", hr));
*errorMessage = msgCannotAddBreakPoint(QLatin1String("<Unknown error>"));
return false;
}
if (!apply(ibp, errorMessage))
return false;
// GetOffset can fail when attaching to remote processes.
hr = ibp->GetOffset(address);
if (FAILED(hr))
*address = 0;
hr = ibp->GetId(id);
if (FAILED(hr)) {
*errorMessage = msgCannotAddBreakPoint(msgComFailed("GetId", hr));
}
// Make sure file can be found in editor manager and text markers
// Use '/' and capitalize drive letter
QString CDBBreakPoint::canonicalSourceFile(const QString &f)
{
if (f.isEmpty())
return f;
QString rc = QDir::fromNativeSeparators(f);
if (rc.size() > 2 && rc.at(1) == QLatin1Char(':'))
rc[0] = rc.at(0).toUpper();
return rc;
}
bool CDBBreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
{
clear();
WCHAR wszBuf[MAX_PATH];
const HRESULT hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint: %1").
arg(msgComFailed("GetOffsetExpressionWide", hr));
return false;
}
// Pass Count is ignoreCount + 1
ibp->GetPassCount(&ignoreCount);
if (ignoreCount)
ignoreCount--;
ULONG flags = 0;
ibp->GetFlags(&flags);
oneShot = (flags & DEBUG_BREAKPOINT_ONE_SHOT);
enabled = (flags & DEBUG_BREAKPOINT_ENABLED);

axasia
committed
const QString expr = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
if (!parseExpression(expr)) {
*errorMessage = QString::fromLatin1("Parsing of '%1' failed.").arg(expr);
return false;
}
return true;
}
bool CDBBreakPoint::parseExpression(const QString &expr)
{
clearExpressionData();
const QChar sourceFileQuote = QLatin1Char(sourceFileQuoteC);
// Check for file or function
int conditionPos = 0;
if (expr.startsWith(sourceFileQuote)) { // `c:\foo.cpp:523`[ "condition"]
// Do not fall for the drive letter colon here
const int colonPos = expr.indexOf(QLatin1Char(':'), 3);
if (colonPos == -1)
return false;
conditionPos = expr.indexOf(sourceFileQuote, colonPos + 1);
if (conditionPos == -1)
return false;
fileName = canonicalSourceFile(expr.mid(1, colonPos - 1));
const QString lineNumberS = expr.mid(colonPos + 1, conditionPos - colonPos - 1);
bool lineNumberOk = false;
lineNumber = lineNumberS.toInt(&lineNumberOk);
if (!lineNumberOk)
return false;
conditionPos++;
} else {
// Check function token
conditionPos = expr.indexOf(QLatin1Char(' '));
if (conditionPos != -1) {
funcName = expr.mid(0, conditionPos);
conditionPos++;
} else {
funcName = expr;
conditionPos = expr.size();
}
}
// Condition? ".if bla"
if (conditionPos >= expr.size())
return true;
const QChar doubleQuote = QLatin1Char('"');
conditionPos = expr.indexOf(doubleQuote, conditionPos);
if (conditionPos == -1)
return true;
conditionPos++;
const int condEndPos = expr.lastIndexOf(doubleQuote);
if (condEndPos == -1)
return false;
condition = expr.mid(conditionPos, condEndPos - conditionPos);
return true;
}
bool CDBBreakPoint::getBreakPointCount(CIDebugControl* debugControl, ULONG *count, QString *errorMessage /* = 0*/)
{
const HRESULT hr = debugControl->GetNumberBreakpoints(count);
if (FAILED(hr)) {
if (errorMessage)
*errorMessage = QString::fromLatin1("Cannot determine breakpoint count: %1").
arg(msgComFailed("GetNumberBreakpoints", hr));
return false;
}
return true;
}
bool CDBBreakPoint::getBreakPoints(CIDebugControl* debugControl, QList<CDBBreakPoint> *bps, QString *errorMessage)
if (!getBreakPointCount(debugControl, &count, errorMessage))
return false;
// retrieve one by one and parse
for (ULONG b= 0; b < count; b++) {
IDebugBreakpoint2 *ibp = 0;
const HRESULT hr = debugControl->GetBreakpointByIndex2(b, &ibp);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint %1: %2").
arg(b).arg(msgComFailed("GetBreakpointByIndex2", hr));
return false;
}
CDBBreakPoint bp;
if (!bp.retrieve(ibp, errorMessage))
return false;
bps->push_back(bp);
}
return true;
}
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
// Find a breakpoint by id
static inline QString msgNoBreakPointWithId(unsigned long id, const QString &why)
{
return QString::fromLatin1("Unable to find breakpoint with id %1: %2").arg(id).arg(why);
}
static IDebugBreakpoint2 *breakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage)
{
CIDebugBreakpoint *ibp = 0;
const HRESULT hr = ctl->GetBreakpointById2(id, &ibp);
if (FAILED(hr)) {
*errorMessage = msgNoBreakPointWithId(id, msgComFailed("GetBreakpointById2", hr));
return 0;
}
if (!ibp) {
*errorMessage = msgNoBreakPointWithId(id, QLatin1String("<not found>"));
return 0;
}
return ibp;
}
// Remove breakpoint by id
static bool removeBreakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage)
{
if (debugBP)
qDebug() << Q_FUNC_INFO << id;
CIDebugBreakpoint *ibp = breakPointById(ctl, id, errorMessage);
if (!ibp)
return false;
const HRESULT hr = ctl->RemoveBreakpoint2(ibp);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Cannot remove breakpoint %1: %2").arg(id).arg(msgComFailed("RemoveBreakpointById2", hr));
return false;
}
return true;
}
// Set enabled by id
// Change enabled state of a breakpoint by id
static inline QString msgCannotSetBreakPointEnabled(unsigned long id, bool enabled, const QString &why)
{
return QString::fromLatin1("Cannot %1 breakpoint %2: %3").
arg(QLatin1String(enabled ? "enable" : "disable")).arg(id).arg(why);
}
static bool setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool enabled, QString *errorMessage)
{
if (debugBP)
qDebug() << Q_FUNC_INFO << id << enabled;
CIDebugBreakpoint *ibp = breakPointById(ctl, id, errorMessage);
if (!ibp) {
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, *errorMessage);
return false;
}
// Compare flags
ULONG flags;
HRESULT hr = ibp->GetFlags(&flags);
if (FAILED(hr)) {
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, msgComFailed("GetFlags", hr));
return false;
}
const bool wasEnabled = (flags & DEBUG_BREAKPOINT_ENABLED);
if (wasEnabled == enabled)
return true;
// Set new value
if (enabled) {
flags |= DEBUG_BREAKPOINT_ENABLED;
} else {
flags &= ~DEBUG_BREAKPOINT_ENABLED;
}
hr = ibp->SetFlags(flags);
if (FAILED(hr)) {
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, msgComFailed("SetFlags", hr));
return false;
}
return true;
}
// Synchronize (halted) engine breakpoints with those of the BreakHandler.
bool CDBBreakPoint::synchronizeBreakPoints(CIDebugControl* debugControl,
CIDebugSymbols *syms,
BreakHandler *handler,
QString *errorMessage)
{
// Do an initial check whether we are in a state that allows
// for modifying breakPoints
ULONG engineCount;
if (!getBreakPointCount(debugControl, &engineCount, errorMessage)) {
*errorMessage = QString::fromLatin1("Cannot modify breakpoints: %1").arg(*errorMessage);
return false;
}
// Insert new ones
bool updateMarkers = false;
foreach (BreakpointData *nbd, handler->insertedBreakpoints()) {
// Function breakpoints: Are the module names specified?
bool breakPointOk = false;
breakPointOk = true;
} else {
switch (resolveSymbol(syms, &nbd->funcName, errorMessage)) {
case ResolveSymbolOk:
breakPointOk = true;
break;
case ResolveSymbolAmbiguous:
qWarning("Warning: %s\n", qPrintable(*errorMessage));
breakPointOk = true;
break;
case ResolveSymbolNotFound:
case ResolveSymbolError:
qWarning("Warning: %s\n", qPrintable(*errorMessage));
break;
};
} // function breakpoint
// Now add...
if (breakPointOk) {
quint64 address;
unsigned long id;
CDBBreakPoint ncdbbp(*nbd);
breakPointOk = ncdbbp.add(debugControl, &address, &id, errorMessage);
if (breakPointOk) {
if (debugBP)
qDebug() << "Added " << id << " at " << address << ncdbbp;
handler->takeInsertedBreakPoint(nbd);
updateMarkers = true;
nbd->pending = false;
nbd->bpNumber = QString::number(id);
nbd->bpAddress = QLatin1String("0x") + QString::number(address, 16);
// Take over rest as is
nbd->bpCondition = nbd->condition;
nbd->bpIgnoreCount = nbd->ignoreCount;
nbd->bpFileName = nbd->fileName;
nbd->bpLineNumber = nbd->lineNumber;
nbd->bpFuncName = nbd->funcName;
} // had symbol
if (!breakPointOk)
qWarning("%s\n", qPrintable(*errorMessage));
// Delete
foreach (BreakpointData *rbd, handler->takeRemovedBreakpoints()) {
if (!removeBreakPointById(debugControl, rbd->bpNumber.toUInt(), errorMessage))
qWarning("%s\n", qPrintable(*errorMessage));
delete rbd;
// Enable/Disable
foreach (BreakpointData *ebd, handler->takeEnabledBreakpoints())
if (!setBreakPointEnabledById(debugControl, ebd->bpNumber.toUInt(), true, errorMessage))
qWarning("%s\n", qPrintable(*errorMessage));
foreach (BreakpointData *dbd, handler->takeDisabledBreakpoints())
if (!setBreakPointEnabledById(debugControl, dbd->bpNumber.toUInt(), false, errorMessage))
qWarning("%s\n", qPrintable(*errorMessage));
if (updateMarkers)
handler->updateMarkers();
if (debugBP > 1) {
QList<CDBBreakPoint> bps;
CDBBreakPoint::getBreakPoints(debugControl, &bps, errorMessage);
qDebug().nospace() << "### Breakpoints in engine: " << bps;
}
return true;
}