Commit 29beabd5 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Debugger: Handle throw/catch/main as BreakpointType enum values.

Remove BreakHandler::hasPendingBreakpoints, handle new types
in CDB and gdb. Start fixing Breakpoint-Dialog.
Add assignment of Breakpointresponse from BreakpointParameters.

Reviewed-by: hjk
parent 3f366ef5
......@@ -74,16 +74,6 @@ int BreakHandler::rowCount(const QModelIndex &parent) const
return parent.isValid() ? 0 : m_storage.size();
}
// FIXME: Only used by cdb. Move there?
bool BreakHandler::hasPendingBreakpoints() const
{
ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
for ( ; it != et; ++it)
if (it->isPending())
return true;
return false;
}
static inline bool fileNameMatch(const QString &f1, const QString &f2)
{
#ifdef Q_OS_WIN
......@@ -907,11 +897,21 @@ QString BreakHandler::BreakpointItem::toToolTip() const
case BreakpointByAddress:
t = tr("Breakpoint by Address");
break;
case BreakpointAtThrow:
t = tr("Breakpoint at \"throw\"");
break;
case BreakpointAtCatch:
t = tr("Breakpoint at \"catch\"");
break;
case BreakpointAtMain:
t = tr("Breakpoint at Function \"main()\"");
break;
case Watchpoint:
t = tr("Watchpoint");
break;
case UnknownType:
t = tr("Unknown Breakpoint Type");
break;
}
QString rc;
......
......@@ -68,7 +68,6 @@ public:
BreakpointIds engineBreakpointIds(DebuggerEngine *engine) const;
BreakpointIds unclaimedBreakpointIds() const;
int size() const { return m_storage.size(); }
bool hasPendingBreakpoints() const;
// Find a breakpoint matching approximately the data in needle.
BreakpointId findSimilarBreakpoint(const BreakpointResponse &needle) const;
......
......@@ -41,9 +41,6 @@ namespace Internal {
//
//////////////////////////////////////////////////////////////////
const char *BreakpointData::throwFunction = "throw";
const char *BreakpointData::catchFunction = "catch";
BreakpointParameters::BreakpointParameters(BreakpointType t) :
type(t), enabled(true), useFullPath(false),
ignoreCount(0), lineNumber(0), address(0)
......@@ -52,7 +49,7 @@ BreakpointParameters::BreakpointParameters(BreakpointType t) :
bool BreakpointParameters::equals(const BreakpointParameters &rhs) const
{
return type != rhs.type && enabled == rhs.enabled
return type == rhs.type && enabled == rhs.enabled
&& useFullPath == rhs.useFullPath
&& fileName == rhs.fileName && condition == rhs.condition
&& ignoreCount == rhs.ignoreCount && lineNumber == rhs.lineNumber
......@@ -168,5 +165,14 @@ QString BreakpointResponse::toString() const
return result;
}
void BreakpointResponse::fromParameters(const BreakpointParameters &p)
{
BreakpointParameters::operator=(p);
number = 0;
fullName.clear();
multiple = false;
state.clear();
}
} // namespace Internal
} // namespace Debugger
......@@ -62,8 +62,9 @@ enum BreakpointType
BreakpointByFileAndLine,
BreakpointByFunction,
BreakpointByAddress,
//BreakpointAtThrow, // FIXME: actually use this
//BreakpointAtCatch, // FIXME: actually use this
BreakpointAtThrow,
BreakpointAtCatch,
BreakpointAtMain,
Watchpoint,
};
......@@ -135,9 +136,6 @@ public:
bool isWatchpoint() const { return type() == Watchpoint; }
bool isBreakpoint() const { return type() != Watchpoint; } // Enough for now.
// Generic name for function to break on 'throw'
static const char *throwFunction;
static const char *catchFunction;
private:
// All setters return true on change.
......@@ -173,6 +171,8 @@ public:
QString toString() const;
public:
void fromParameters(const BreakpointParameters &p);
int number; // Breakpoint number assigned by the debugger engine.
QString fullName; // Full file name acknowledged by the debugger engine.
bool multiple; // Happens in constructors/gdb.
......
......@@ -69,72 +69,83 @@ public:
explicit BreakpointDialog(QWidget *parent);
bool showDialog(BreakpointData *data);
void setParameters(const BreakpointParameters &p);
BreakpointParameters parameters() const;
public slots:
void typeChanged(int index);
private:
void setType(BreakpointType type);
BreakpointType type() const;
};
BreakpointDialog::BreakpointDialog(QWidget *parent) : QDialog(parent)
{
// match BreakpointType (except unknown type) with additional item
setupUi(this);
comboBoxType->insertItem(0, tr("File and Line Number"));
comboBoxType->insertItem(1, tr("Function Name"));
comboBoxType->insertItem(2, tr("Function \"main()\""));
comboBoxType->insertItem(3, tr("Address"));
QStringList types;
types << tr("File and Line Number") << tr("Function Name") << tr("Address")
<< tr("throw") << tr("catch") << tr("Function \"main()\"")
<< tr("Address (Watchpoint)");
QTC_ASSERT(types.size() == Watchpoint, return; )
comboBoxType->addItems(types);
pathChooserFileName->setExpectedKind(Utils::PathChooser::File);
connect(comboBoxType, SIGNAL(activated(int)), SLOT(typeChanged(int)));
lineEditIgnoreCount->setValidator(
new QIntValidator(0, 2147483647, lineEditIgnoreCount));
}
bool BreakpointDialog::showDialog(BreakpointData *data)
void BreakpointDialog::setType(BreakpointType type)
{
const int comboIndex = type - 1; // Skip UnknownType
if (comboIndex != comboBoxType->currentIndex()) {
comboBoxType->setCurrentIndex(comboIndex);
typeChanged(comboIndex);
}
}
BreakpointType BreakpointDialog::type() const
{
pathChooserFileName->setPath(data->fileName());
lineEditLineNumber->setText(QString::number(data->lineNumber()));
lineEditFunction->setText(data->functionName());
lineEditCondition->setText(QString::fromUtf8(data->condition()));
lineEditIgnoreCount->setText(QString::number(data->ignoreCount()));
checkBoxUseFullPath->setChecked(data->useFullPath());
lineEditThreadSpec->setText(QString::fromUtf8(data->threadSpec()));
const quint64 address = data->address();
const int type = comboBoxType->currentIndex() + 1; // Skip unknown type
return static_cast<BreakpointType>(type);
}
void BreakpointDialog::setParameters(const BreakpointParameters &p)
{
pathChooserFileName->setPath(p.fileName);
lineEditLineNumber->setText(QString::number(p.lineNumber));
lineEditFunction->setText(p.functionName);
lineEditCondition->setText(QString::fromUtf8(p.condition));
lineEditIgnoreCount->setText(QString::number(p.ignoreCount));
checkBoxUseFullPath->setChecked(p.useFullPath);
lineEditThreadSpec->setText(p.threadSpec);
const quint64 address = p.address;
if (address)
lineEditAddress->setText(QString::fromAscii("0x%1").arg(address, 0, 16));
int initialType = 0;
if (!data->functionName().isEmpty())
initialType = data->functionName() == QLatin1String("main") ? 2 : 1;
if (address)
initialType = 3;
typeChanged(initialType);
if (exec() != QDialog::Accepted)
return false;
setType(p.type);
}
// Check if changed.
const int newLineNumber = lineEditLineNumber->text().toInt();
const bool newUseFullPath = checkBoxUseFullPath->isChecked();
const quint64 newAddress = lineEditAddress->text().toULongLong(0, 0);
const QString newFunction = lineEditFunction->text();
const QString newFileName = pathChooserFileName->path();
const QByteArray newCondition = lineEditCondition->text().toUtf8();
const int newIgnoreCount = lineEditIgnoreCount->text().toInt();
const QByteArray newThreadSpec = lineEditThreadSpec->text().toUtf8();
bool result = false;
result |= data->setAddress(newAddress);
result |= data->setFunctionName(newFunction);
result |= data->setUseFullPath(newUseFullPath);
result |= data->setFileName(newFileName);
result |= data->setLineNumber(newLineNumber);
result |= data->setCondition(newCondition);
result |= data->setIgnoreCount(newIgnoreCount);
result |= data->setThreadSpec(newThreadSpec);
return result;
BreakpointParameters BreakpointDialog::parameters() const
{
BreakpointParameters rc(type());
rc.lineNumber = lineEditLineNumber->text().toInt();
rc.useFullPath = checkBoxUseFullPath->isChecked();
rc.address = lineEditAddress->text().toULongLong(0, 0);
rc.functionName = lineEditFunction->text();
rc.fileName = pathChooserFileName->path();
rc.condition = lineEditCondition->text().toUtf8();
rc.ignoreCount = lineEditIgnoreCount->text().toInt();
rc.threadSpec = lineEditThreadSpec->text().toUtf8();
return rc;
}
void BreakpointDialog::typeChanged(int index)
void BreakpointDialog::typeChanged(int)
{
const bool isLineVisible = index == 0;
const bool isFunctionVisible = index == 1;
const bool isAddressVisible = index == 3;
const BreakpointType t = type();
const bool isLineVisible = t == BreakpointByFileAndLine;
const bool isFunctionVisible = t == BreakpointByFunction || t == BreakpointAtMain;
const bool isAddressVisible = t == BreakpointByAddress || t == Watchpoint;
labelFileName->setEnabled(isLineVisible);
pathChooserFileName->setEnabled(isLineVisible);
labelLineNumber->setEnabled(isLineVisible);
......@@ -145,10 +156,34 @@ void BreakpointDialog::typeChanged(int index)
lineEditFunction->setEnabled(isFunctionVisible);
labelAddress->setEnabled(isAddressVisible);
lineEditAddress->setEnabled(isAddressVisible);
if (index == 2)
if (t == BreakpointAtMain)
lineEditFunction->setText(QLatin1String("main"));
}
bool BreakpointDialog::showDialog(BreakpointData *data)
{
setParameters(data->parameters());
if (exec() != QDialog::Accepted)
return false;
// Check if changed.
const BreakpointParameters newParameters = parameters();
if (newParameters == data->parameters())
return false;
bool result = false;
result |= data->setType(newParameters.type);
result |= data->setAddress(newParameters.address);
result |= data->setFunctionName(newParameters.functionName);
result |= data->setUseFullPath(newParameters.useFullPath);
result |= data->setFileName(newParameters.fileName);
result |= data->setLineNumber(newParameters.lineNumber);
result |= data->setCondition(newParameters.condition);
result |= data->setIgnoreCount(newParameters.ignoreCount);
result |= data->setThreadSpec(newParameters.threadSpec);
return result;
}
///////////////////////////////////////////////////////////////////////
//
// BreakWindow
......@@ -292,7 +327,6 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
synchronizeAction->setEnabled(debuggerCore()->hasSnapshots());
QModelIndex idx0 = (si.size() ? si.front() : QModelIndex());
QModelIndex idx2 = idx0.sibling(idx0.row(), 2);
const BreakpointId id = handler->findBreakpointByIndex(idx0);
bool enabled = si.isEmpty() || handler->isEnabled(id);
......@@ -363,14 +397,9 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
else if (act == addBreakpointAction)
addBreakpoint();
else if (act == breakAtThrowAction) {
BreakpointData data(BreakpointByFunction);
data.setFunctionName(BreakpointData::throwFunction);
handler->appendBreakpoint(data);
handler->appendBreakpoint(BreakpointData(BreakpointAtThrow));
} else if (act == breakAtCatchAction) {
// FIXME: Use the proper breakpoint type instead.
BreakpointData data(BreakpointByFunction);
data.setFunctionName(BreakpointData::catchFunction);
handler->appendBreakpoint(data);
handler->appendBreakpoint(BreakpointData(BreakpointAtCatch));
}
}
......
......@@ -40,27 +40,27 @@ namespace Debugger {
namespace Internal {
// Convert breakpoint structs
static CdbCore::BreakPoint breakPointFromBreakPointData(const BreakpointData &bpd, const QString &functionName)
static CdbCore::BreakPoint breakPointFromBreakPointData(const BreakpointParameters &bpd, const QString &functionName)
{
CdbCore::BreakPoint rc;
rc.type = bpd.type() == Watchpoint ?
rc.type = bpd.type == Watchpoint ?
CdbCore::BreakPoint::Data :
CdbCore::BreakPoint::Code ;
rc.address = bpd.address();
if (!bpd.threadSpec().isEmpty()) {
rc.address = bpd.address;
if (!bpd.threadSpec.isEmpty()) {
bool ok;
rc.threadId = bpd.threadSpec().toInt(&ok);
rc.threadId = bpd.threadSpec.toInt(&ok);
if (!ok)
qWarning("Cdb: Cannot convert breakpoint thread specification '%s'", bpd.threadSpec().constData());
qWarning("Cdb: Cannot convert breakpoint thread specification '%s'", bpd.threadSpec.constData());
}
rc.fileName = QDir::toNativeSeparators(bpd.fileName());
rc.condition = bpd.condition();
rc.funcName = functionName.isEmpty() ? bpd.functionName() : functionName;
rc.ignoreCount = bpd.ignoreCount();
rc.lineNumber = bpd.lineNumber();
rc.fileName = QDir::toNativeSeparators(bpd.fileName);
rc.condition = bpd.condition;
rc.funcName = functionName.isEmpty() ? bpd.functionName : functionName;
rc.ignoreCount = bpd.ignoreCount;
rc.lineNumber = bpd.lineNumber;
rc.oneShot = false;
rc.enabled = bpd.isEnabled();
rc.enabled = bpd.enabled;
return rc;
}
......@@ -69,31 +69,38 @@ static inline QString msgCannotSetBreakAtFunction(const QString &func, const QSt
return QString::fromLatin1("Cannot set a breakpoint at '%1': %2").arg(func, why);
}
void setBreakpointResponse(const BreakpointData *nbd, int number, BreakpointResponse *response)
static inline BreakpointParameters transformBreakpoint(const BreakpointParameters &p)
{
response->address = nbd->address();
response->number = number;
response->functionName = nbd->functionName();
response->type = nbd->type();
response->condition = nbd->condition();
response->ignoreCount = nbd->ignoreCount();
response->fullName = response->fileName = nbd->fileName();
response->lineNumber = nbd->lineNumber();
response->threadSpec = nbd->threadSpec();
response->enabled = nbd->isEnabled();
if (p.type == BreakpointAtThrow) {
BreakpointParameters rc(BreakpointByFunction);
rc.functionName = QLatin1String(cdbThrowFunction);
return rc;
}
if (p.type == BreakpointAtCatch) {
BreakpointParameters rc(BreakpointByFunction);
rc.functionName = QLatin1String(cdbCatchFunction);
return rc;
}
if (p.type == BreakpointAtMain) {
BreakpointParameters rc(BreakpointByFunction);
rc.functionName = QLatin1String("main");
return rc;
}
return p;
}
bool addCdbBreakpoint(CIDebugControl* debugControl,
CIDebugSymbols *syms,
const BreakpointData *nbd,
const BreakpointParameters &bpIn,
BreakpointResponse *response,
QString *errorMessage)
{
const BreakpointParameters bp = transformBreakpoint(bpIn);
errorMessage->clear();
// Function breakpoints: Are the module names specified?
QString resolvedFunction;
if (nbd->type() == BreakpointByFunction) {
resolvedFunction = nbd->functionName();
if (bp.type == BreakpointByFunction) {
resolvedFunction = bp.functionName;
switch (resolveSymbol(syms, &resolvedFunction, errorMessage)) {
case ResolveSymbolOk:
break;
......@@ -101,21 +108,23 @@ bool addCdbBreakpoint(CIDebugControl* debugControl,
break;
case ResolveSymbolNotFound:
case ResolveSymbolError:
*errorMessage = msgCannotSetBreakAtFunction(nbd->functionName(), *errorMessage);
*errorMessage = msgCannotSetBreakAtFunction(bp.functionName, *errorMessage);
return false;
}
if (debugBreakpoints)
qDebug() << nbd->functionName() << " resolved to " << resolvedFunction;
qDebug() << bp.functionName << " resolved to " << resolvedFunction;
} // function breakpoint
// Now add...
quint64 address;
unsigned long id;
const CdbCore::BreakPoint ncdbbp = breakPointFromBreakPointData(*nbd, resolvedFunction);
const CdbCore::BreakPoint ncdbbp = breakPointFromBreakPointData(bp, resolvedFunction);
if (!ncdbbp.add(debugControl, errorMessage, &id, &address))
return false;
if (debugBreakpoints)
qDebug("Added %lu at 0x%lx %s", id, address, qPrintable(ncdbbp.toString()));
setBreakpointResponse(nbd, id, response);
response->fromParameters(bp);
response->number = id;
response->address = address;
response->functionName = resolvedFunction;
return true;
......
......@@ -40,13 +40,13 @@ QT_END_NAMESPACE
namespace Debugger {
namespace Internal {
class BreakpointData;
class BreakpointParameters;
class BreakpointResponse;
// Convert breakpoint structs
bool addCdbBreakpoint(CIDebugControl* debugControl,
CIDebugSymbols *syms,
const BreakpointData *nbd,
const BreakpointParameters &bp,
BreakpointResponse *response,
QString *errorMessage);
......@@ -54,9 +54,6 @@ bool deleteCdbBreakpoints(CIDebugControl* debugControl, QString *errorMessage);
void debugCdbBreakpoints(CIDebugControl* debugControl);
// Set response from data.
void setBreakpointResponse(const BreakpointData *nbd, int number, BreakpointResponse *response);
} // namespace Internal
} // namespace Debugger
......
......@@ -531,11 +531,7 @@ void CdbEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG64 ini
}
// Clear any saved breakpoints and set initial breakpoints
m_engine->executeDebuggerCommand(QLatin1String("bc"));
if (m_engine->breakHandler()->hasPendingBreakpoints()) {
if (debugCDBExecution)
qDebug("processCreatedAttached: Syncing breakpoints");
m_engine->attemptBreakpointSynchronization();
}
m_engine->attemptBreakpointSynchronization();
// Attaching to crashed: This handshake (signalling an event) is required for
// the exception to be delivered to the debugger
// Also, see special handling in slotModulesLoaded().
......@@ -1318,7 +1314,7 @@ bool CdbEngine::attemptBreakpointSynchronizationI(QString *errorMessage)
switch (handler->state(id)) {
case BreakpointInsertRequested:
handler->setState(id, BreakpointInsertProceeding);
if (addCdbBreakpoint(control, symbols, data, &response, errorMessage)) {
if (addCdbBreakpoint(control, symbols, data->parameters(), &response, errorMessage)) {
notifyBreakpointInsertOk(id);
handler->setResponse(id, response);
} else {
......@@ -1330,7 +1326,7 @@ bool CdbEngine::attemptBreakpointSynchronizationI(QString *errorMessage)
// Skip disabled breakpoints, else add
handler->setState(id, BreakpointChangeProceeding);
if (data->isEnabled()) {
if (addCdbBreakpoint(control, symbols, data, &response, errorMessage)) {
if (addCdbBreakpoint(control, symbols, data->parameters(), &response, errorMessage)) {
notifyBreakpointChangeOk(id);
handler->setResponse(id, response);
} else {
......@@ -1348,7 +1344,7 @@ bool CdbEngine::attemptBreakpointSynchronizationI(QString *errorMessage)
case BreakpointPending:
// Existing breakpoints were deleted due to change/removal, re-set
if (syncType == BreakpointsRemovedChanged
&& !addCdbBreakpoint(control, symbols, handler->breakpointById(id), &response, errorMessage))
&& !addCdbBreakpoint(control, symbols, handler->breakpointById(id)->parameters(), &response, errorMessage))
showMessage(*errorMessage, LogError);
break;
default:
......
......@@ -173,6 +173,9 @@ bool searchSymbols(CIDebugSymbols *syms, const QString &pattern,
return true;
}
const char *cdbThrowFunction = "CxxThrowException";
const char *cdbCatchFunction = "__CxxCallCatchBlock";
// Helper for the resolveSymbol overloads.
static ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol,
QStringList *matches,
......@@ -182,15 +185,9 @@ static ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol,
// Is it an incomplete symbol?
if (symbol->contains(QLatin1Char('!')))
return ResolveSymbolOk;
// Throw and catch
bool withinMSVCRunTime = false;
if (*symbol == QLatin1String(BreakpointData::throwFunction)) {
*symbol = QLatin1String("CxxThrowException");
withinMSVCRunTime = true;
} else if (*symbol == QLatin1String(BreakpointData::catchFunction)) {
*symbol = QLatin1String("__CxxCallCatchBlock");
withinMSVCRunTime = true;
} else if (*symbol == QLatin1String("qMain")) // 'main' is a #define for gdb, but not for VS
const bool withinMSVCRunTime = *symbol == QLatin1String(cdbThrowFunction)
|| *symbol == QLatin1String(cdbCatchFunction);
if (*symbol == QLatin1String("qMain")) // 'main' is a #define for gdb, but not for VS
*symbol = QLatin1String("main");
// resolve
if (!searchSymbols(syms, *symbol, matches, errorMessage))
......
......@@ -64,6 +64,9 @@ ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, const QString &pattern,
bool getModuleSymbols(CIDebugSymbols *syms, const QString &moduleName,
QList<Symbol> *symbols, QString *errorMessage);
extern const char *cdbThrowFunction;
extern const char *cdbCatchFunction;
} // namespace Internal
} // namespace Debugger
......
......@@ -2156,25 +2156,32 @@ static QByteArray addressSpec(quint64 address)
QByteArray GdbEngine::breakpointLocation(BreakpointId id)
{
BreakHandler *handler = breakHandler();
QByteArray functionName = handler->functionName(id).toUtf8();
if (!functionName.isEmpty()) {
// FIXME: Use the types.
if (functionName == BreakpointData::throwFunction)
return "__cxa_throw";
if (functionName == BreakpointData::catchFunction)
return "__cxa_begin_catch";
const BreakpointData *data = handler->breakpointById(id);
QTC_ASSERT(data, return QByteArray());
const BreakpointParameters parameters = data->parameters();
// FIXME: Non-GCC-runtime
if (parameters.type == BreakpointAtThrow)
return "__cxa_throw";
if (parameters.type == BreakpointAtCatch)
return "__cxa_begin_catch";
if (parameters.type == BreakpointAtMain)
#ifdef Q_OS_WIN
return "qMain";
#else
return "main";
#endif
const QByteArray functionName = parameters.functionName.toUtf8();
if (!functionName.isEmpty())
return functionName;
}
quint64 address = handler->address(id);
if (address)
if (const quint64 address = handler->address(id))
return addressSpec(address);
// In this case, data->funcName is something like '*0xdeadbeef'
int lineNumber = handler->lineNumber(id);
const int lineNumber = handler->lineNumber(id);
if (lineNumber == 0)
return functionName;
QString fileName = handler->fileName(id);
if (handler->useFullPath(id))
fileName = breakLocation(fileName);
const QString fileName = parameters.useFullPath ?
breakLocation(parameters.fileName) :
parameters.fileName;
// The argument is simply a C-quoted version of the argument to the
// non-MI "break" command, including the "original" quoting it wants.
return "\"\\\"" + GdbMi::escapeCString(fileName).toLocal8Bit() + "\\\":"
......
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