Commit 90657a49 authored by hjk's avatar hjk
Browse files

debugger: small usability fixes for dissassembler, breakpoint view, location

parent 714f7656
......@@ -34,6 +34,7 @@
#include "debuggercore.h"
#include "debuggerengine.h"
#include "debuggerstringutils.h"
#include "stackframe.h"
#include <utils/qtcassert.h>
......@@ -140,7 +141,7 @@ BreakpointId BreakHandler::findSimilarBreakpoint(const BreakpointResponse &needl
const BreakpointId id = it.key();
const BreakpointParameters &data = it->data;
const BreakpointResponse &response = it->response;
qDebug() << "COMPARING " << data.toString() << " WITH " << needle.toString();
//qDebug() << "COMPARING " << data.toString() << " WITH " << needle.toString();
if (response.number && response.number == needle.number)
return id;
......@@ -353,10 +354,10 @@ BreakpointId BreakHandler::findBreakpointByIndex(const QModelIndex &index) const
BreakpointIds BreakHandler::findBreakpointsByIndex(const QList<QModelIndex> &list) const
{
BreakpointIds ids;
QSet<BreakpointId> ids;
foreach (const QModelIndex &index, list)
ids.append(findBreakpointByIndex(index));
return ids;
ids.insert(findBreakpointByIndex(index));
return ids.toList();
}
Qt::ItemFlags BreakHandler::flags(const QModelIndex &index) const
......@@ -467,7 +468,7 @@ QVariant BreakHandler::data(const QModelIndex &mi, int role) const
if (role == Qt::DisplayRole) {
QString displayValue;
const quint64 address =
data.isWatchpoint() ? data.address : response.address;
it->isPending() ? data.address : response.address;
if (address)
displayValue += QString::fromAscii("0x%1").arg(address, 0, 16);
if (!response.extra.isEmpty()) {
......@@ -842,8 +843,17 @@ void BreakHandler::gotoLocation(BreakpointId id) const
{
ConstIterator it = m_storage.find(id);
QTC_ASSERT(it != m_storage.end(), return);
debuggerCore()->gotoLocation(
it->data.fileName, it->data.lineNumber, false);
if (it->data.type == BreakpointByAddress) {
StackFrame frame;
frame.address = it->data.address;
DebuggerEngine *engine = debuggerCore()->currentEngine();
if (engine)
engine->gotoLocation(frame, false);
} else {
const QString fileName = it->markerFileName();
const int lineNumber = it->markerLineNumber();
debuggerCore()->gotoLocation(fileName, lineNumber, false);
}
}
void BreakHandler::updateLineNumberFromMarker(BreakpointId id, int lineNumber)
......
......@@ -74,13 +74,13 @@ QString BreakpointParameters::toString() const
{
QString result;
QTextStream ts(&result);
ts << fileName;
ts << condition;
ts << ignoreCount;
ts << lineNumber;
ts << address;
ts << functionName;
ts << useFullPath;
ts << " FileName: " << fileName;
ts << " Condition: " << condition;
ts << " IgnoreCount: " << ignoreCount;
ts << " LineNumber: " << lineNumber;
ts << " Address: " << address;
ts << " FunctionName: " << functionName;
ts << " UseFullPath: " << useFullPath;
return result;
}
......@@ -99,12 +99,12 @@ QString BreakpointResponse::toString() const
{
QString result;
QTextStream ts(&result);
ts << number;
ts << pending;
ts << fullName;
ts << multiple;
ts << extra;
ts << correctedLineNumber;
ts << " Number: " << number;
ts << " Pending: " << pending;
ts << " FullName: " << fullName;
ts << " Multiple: " << multiple;
ts << " Extra: " << extra;
ts << " CorrectedLineNumber: " << correctedLineNumber;
return result + BreakpointParameters::toString();
}
......
......@@ -335,15 +335,6 @@ void BreakWindow::showAddressColumn(bool on)
setColumnHidden(7, !on);
}
static QModelIndexList normalizeIndexes(const QModelIndexList &list)
{
QModelIndexList res;
foreach (const QModelIndex &index, list)
if (index.column() == 0)
res.append(index);
return res;
}
void BreakWindow::keyPressEvent(QKeyEvent *ev)
{
if (ev->key() == Qt::Key_Delete) {
......@@ -351,8 +342,11 @@ void BreakWindow::keyPressEvent(QKeyEvent *ev)
QTC_ASSERT(sm, return);
QModelIndexList si = sm->selectedIndexes();
if (si.isEmpty())
si.append(currentIndex().sibling(currentIndex().row(), 0));
deleteBreakpoints(normalizeIndexes(si));
si.append(currentIndex());
const BreakpointIds ids = breakHandler()->findBreakpointsByIndex(si);
int row = qMin(model()->rowCount() - ids.size() - 1, currentIndex().row());
deleteBreakpoints(ids);
setCurrentIndex(si.at(0).sibling(row, 0));
}
QTreeView::keyPressEvent(ev);
}
......@@ -365,8 +359,10 @@ void BreakWindow::resizeEvent(QResizeEvent *ev)
void BreakWindow::mouseDoubleClickEvent(QMouseEvent *ev)
{
QModelIndex indexUnderMouse = indexAt(ev->pos());
if (indexUnderMouse.isValid() && indexUnderMouse.column() >= 4)
editBreakpoints(QModelIndexList() << indexUnderMouse);
if (indexUnderMouse.isValid() && indexUnderMouse.column() >= 4) {
BreakpointId id = breakHandler()->findBreakpointByIndex(indexUnderMouse);
editBreakpoints(BreakpointIds() << id);
}
QTreeView::mouseDoubleClickEvent(ev);
}
......@@ -375,34 +371,35 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
QMenu menu;
QItemSelectionModel *sm = selectionModel();
QTC_ASSERT(sm, return);
QModelIndexList si = sm->selectedIndexes();
QModelIndexList selectedIndices = sm->selectedIndexes();
QModelIndex indexUnderMouse = indexAt(ev->pos());
if (si.isEmpty() && indexUnderMouse.isValid())
si.append(indexUnderMouse.sibling(indexUnderMouse.row(), 0));
si = normalizeIndexes(si);
if (selectedIndices.isEmpty() && indexUnderMouse.isValid())
selectedIndices.append(indexUnderMouse);
BreakHandler *handler = breakHandler();
BreakpointIds selectedIds = handler->findBreakpointsByIndex(selectedIndices);
const int rowCount = model()->rowCount();
const unsigned engineCapabilities = BreakOnThrowAndCatchCapability;
BreakHandler *handler = breakHandler();
// FIXME BP: model()->data(QModelIndex(), EngineCapabilitiesRole).toUInt();
QAction *deleteAction = new QAction(tr("Delete Breakpoint"), &menu);
deleteAction->setEnabled(si.size() > 0);
deleteAction->setEnabled(!selectedIds.isEmpty());
QAction *deleteAllAction = new QAction(tr("Delete All Breakpoints"), &menu);
deleteAllAction->setEnabled(model()->rowCount() > 0);
// Delete by file: Find indices of breakpoints of the same file.
QAction *deleteByFileAction = 0;
QList<QModelIndex> breakPointsOfFile;
BreakpointIds breakpointsInFile;
if (indexUnderMouse.isValid()) {
const QModelIndex index = indexUnderMouse.sibling(indexUnderMouse.row(), 2);
const QString file = model()->data(index).toString();
const QString file = index.data().toString();
if (!file.isEmpty()) {
for (int i = 0; i < rowCount; i++)
if (model()->data(model()->index(i, 2)).toString() == file)
breakPointsOfFile.push_back(model()->index(i, 2));
if (breakPointsOfFile.size() > 1) {
if (index.data().toString() == file)
breakpointsInFile.append(handler->findBreakpointByIndex(index));
if (breakpointsInFile.size() > 1) {
deleteByFileAction =
new QAction(tr("Delete Breakpoints of \"%1\"").arg(file), &menu);
deleteByFileAction->setEnabled(true);
......@@ -425,7 +422,7 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
QAction *editBreakpointAction =
new QAction(tr("Edit Breakpoint..."), &menu);
editBreakpointAction->setEnabled(si.size() > 0);
editBreakpointAction->setEnabled(!selectedIds.isEmpty());
int threadId = 0;
// FIXME BP: m_engine->threadsHandler()->currentThreadId();
......@@ -433,18 +430,15 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
? tr("Associate Breakpoint With All Threads")
: tr("Associate Breakpoint With Thread %1").arg(threadId);
QAction *associateBreakpointAction = new QAction(associateTitle, &menu);
associateBreakpointAction->setEnabled(si.size() > 0);
associateBreakpointAction->setEnabled(!selectedIds.isEmpty());
QAction *synchronizeAction =
new QAction(tr("Synchronize Breakpoints"), &menu);
synchronizeAction->setEnabled(debuggerCore()->hasSnapshots());
QModelIndex idx0 = (si.size() ? si.front() : QModelIndex());
const BreakpointId id = handler->findBreakpointByIndex(idx0);
bool enabled = selectedIds.isEmpty() || handler->isEnabled(selectedIds.at(0));
bool enabled = si.isEmpty() || handler->isEnabled(id);
const QString str5 = si.size() > 1
const QString str5 = selectedIds.size() > 1
? enabled
? tr("Disable Selected Breakpoints")
: tr("Enable Selected Breakpoints")
......@@ -452,7 +446,7 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
? tr("Disable Breakpoint")
: tr("Enable Breakpoint");
QAction *toggleEnabledAction = new QAction(str5, &menu);
toggleEnabledAction->setEnabled(si.size() > 0);
toggleEnabledAction->setEnabled(!selectedIds.isEmpty());
QAction *addBreakpointAction =
new QAction(tr("Add Breakpoint..."), this);
......@@ -486,27 +480,24 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
QAction *act = menu.exec(ev->globalPos());
if (act == deleteAction) {
deleteBreakpoints(si);
} else if (act == deleteAllAction) {
QList<QModelIndex> allRows;
for (int i = 0; i < rowCount; i++)
allRows.push_back(model()->index(i, 0));
deleteBreakpoints(allRows);
} else if (act == deleteByFileAction)
deleteBreakpoints(breakPointsOfFile);
if (act == deleteAction)
deleteBreakpoints(selectedIds);
else if (act == deleteAllAction)
deleteBreakpoints(handler->allBreakpointIds());
else if (act == deleteByFileAction)
deleteBreakpoints(breakpointsInFile);
else if (act == adjustColumnAction)
resizeColumnsToContents();
else if (act == alwaysAdjustAction)
setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
else if (act == editBreakpointAction)
editBreakpoints(si);
editBreakpoints(selectedIds);
else if (act == associateBreakpointAction)
associateBreakpoint(si, threadId);
associateBreakpoint(selectedIds, threadId);
else if (act == synchronizeAction)
; //synchronizeBreakpoints();
else if (act == toggleEnabledAction)
setBreakpointsEnabled(si, !enabled);
setBreakpointsEnabled(selectedIds, !enabled);
else if (act == addBreakpointAction)
addBreakpoint();
else if (act == breakAtThrowAction)
......@@ -515,24 +506,17 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
handler->appendBreakpoint(BreakpointParameters(BreakpointAtCatch));
}
void BreakWindow::setBreakpointsEnabled(const QModelIndexList &list, bool enabled)
void BreakWindow::setBreakpointsEnabled(const BreakpointIds &ids, bool enabled)
{
BreakHandler *handler = breakHandler();
foreach (const BreakpointId id, handler->findBreakpointsByIndex(list))
foreach (const BreakpointId id, ids)
handler->setEnabled(id, enabled);
}
void BreakWindow::setBreakpointsFullPath(const QModelIndexList &list, bool fullpath)
void BreakWindow::deleteBreakpoints(const BreakpointIds &ids)
{
BreakHandler *handler = breakHandler();
foreach (const BreakpointId id, handler->findBreakpointsByIndex(list))
handler->setUseFullPath(id, fullpath);
}
void BreakWindow::deleteBreakpoints(const QModelIndexList &list)
{
BreakHandler *handler = breakHandler();
foreach (const BreakpointId id, handler->findBreakpointsByIndex(list))
foreach (const BreakpointId id, ids)
handler->removeBreakpoint(id);
}
......@@ -552,15 +536,13 @@ void BreakWindow::addBreakpoint()
breakHandler()->appendBreakpoint(data);
}
void BreakWindow::editBreakpoints(const QModelIndexList &list)
void BreakWindow::editBreakpoints(const BreakpointIds &ids)
{
QTC_ASSERT(!list.isEmpty(), return);
QTC_ASSERT(!ids.isEmpty(), return);
BreakHandler *handler = breakHandler();
const BreakpointIds ids = handler->findBreakpointsByIndex(list);
const BreakpointId id = ids.at(0);
if (list.size() == 1) {
if (ids.size() == 1) {
editBreakpoint(id, this);
return;
}
......@@ -573,6 +555,7 @@ void BreakWindow::editBreakpoints(const QModelIndexList &list)
ui.lineEditIgnoreCount->setValidator(
new QIntValidator(0, 2147483647, ui.lineEditIgnoreCount));
BreakHandler *handler = breakHandler();
const QString oldCondition = QString::fromLatin1(handler->condition(id));
const QString oldIgnoreCount = QString::number(handler->ignoreCount(id));
const QString oldThreadSpec = QString::fromLatin1(handler->threadSpec(id));
......@@ -588,23 +571,22 @@ void BreakWindow::editBreakpoints(const QModelIndexList &list)
const QString newIgnoreCount = ui.lineEditIgnoreCount->text();
const QString newThreadSpec = ui.lineEditThreadSpec->text();
// Unchanged -> cancel
if (newCondition == oldCondition && newIgnoreCount == oldIgnoreCount
&& newThreadSpec == oldThreadSpec)
return;
foreach (const BreakpointId id, handler->findBreakpointsByIndex(list)) {
foreach (const BreakpointId id, ids) {
handler->setCondition(id, newCondition.toLatin1());
handler->setIgnoreCount(id, newIgnoreCount.toInt());
handler->setThreadSpec(id, newThreadSpec.toLatin1());
}
}
void BreakWindow::associateBreakpoint(const QModelIndexList &list, int threadId)
void BreakWindow::associateBreakpoint(const BreakpointIds &ids, int threadId)
{
BreakHandler *handler = breakHandler();
QByteArray spec = QByteArray::number(threadId);
foreach (const BreakpointId id, handler->findBreakpointsByIndex(list))
foreach (const BreakpointId id, ids)
handler->setThreadSpec(id, spec);
}
......
......@@ -61,12 +61,11 @@ private:
void keyPressEvent(QKeyEvent *ev);
void mouseDoubleClickEvent(QMouseEvent *ev);
void deleteBreakpoints(const QModelIndexList &list);
void deleteBreakpoints(const BreakpointIds &ids);
void addBreakpoint();
void editBreakpoints(const QModelIndexList &list);
void associateBreakpoint(const QModelIndexList &list, int thread);
void setBreakpointsEnabled(const QModelIndexList &list, bool enabled);
void setBreakpointsFullPath(const QModelIndexList &list, bool fullpath);
void editBreakpoints(const BreakpointIds &ids);
void associateBreakpoint(const BreakpointIds &ids, int thread);
void setBreakpointsEnabled(const BreakpointIds &ids, bool enabled);
bool m_alwaysResizeColumnsToContents;
};
......
......@@ -210,6 +210,7 @@ struct DisassemblerViewAgentPrivate
QPointer<TextEditor::ITextEditor> editor;
StackFrame frame;
bool tryMixed;
bool setMarker;
QPointer<DebuggerEngine> engine;
LocationMark2 *locationMark;
QHash<QString, QString> cache;
......@@ -219,6 +220,7 @@ struct DisassemblerViewAgentPrivate
DisassemblerViewAgentPrivate::DisassemblerViewAgentPrivate() :
editor(0),
tryMixed(true),
setMarker(true),
locationMark(new LocationMark2),
mimeType(_("text/x-qtcreator-generic-asm"))
{
......@@ -279,9 +281,11 @@ bool DisassemblerViewAgent::isMixed() const
&& d->frame.function != _("??");
}
void DisassemblerViewAgent::setFrame(const StackFrame &frame, bool tryMixed)
void DisassemblerViewAgent::setFrame(const StackFrame &frame, bool tryMixed,
bool setMarker)
{
d->frame = frame;
d->setMarker = setMarker;
d->tryMixed = tryMixed;
if (isMixed()) {
QHash<QString, QString>::ConstIterator it = d->cache.find(frameKey(frame));
......@@ -342,7 +346,7 @@ static QPair<int, int> lineNumberOfAddress(const QString &disassembly, quint64 a
const int size = disassembly.size();
for (int lineNumber = 1; pos < size; lineNumber++) {
int endOfLinePos = disassembly.indexOf(newLine, pos + 1);
int endOfLinePos = disassembly.indexOf(newLine, pos);
if (endOfLinePos == -1)
endOfLinePos = size;
const QString line = disassembly.mid(pos, endOfLinePos - pos);
......@@ -382,12 +386,15 @@ void DisassemblerViewAgent::setContents(const QString &contents)
plainTextEdit->setReadOnly(true);
}
d->editor->markableInterface()->removeMark(d->locationMark);
if (d->setMarker)
d->editor->markableInterface()->removeMark(d->locationMark);
d->editor->setDisplayName(_("Disassembler (%1)").arg(d->frame.function));
const QPair<int, int> lineNumberPos = lineNumberOfAddress(contents, d->frame.address);
const QPair<int, int> lineNumberPos =
lineNumberOfAddress(contents, d->frame.address);
if (lineNumberPos.first > 0) {
d->editor->markableInterface()->addMark(d->locationMark, lineNumberPos.first);
if (d->setMarker)
d->editor->markableInterface()->addMark(d->locationMark, lineNumberPos.first);
if (plainTextEdit) {
QTextCursor tc = plainTextEdit->textCursor();
tc.setPosition(lineNumberPos.second);
......@@ -399,7 +406,6 @@ void DisassemblerViewAgent::setContents(const QString &contents)
bool DisassemblerViewAgent::contentsCoversAddress(const QString &contents) const
{
QTC_ASSERT(d, return false);
return lineNumberOfAddress(contents, d->frame.address).first > 0;
}
......
......@@ -83,7 +83,7 @@ public:
explicit DisassemblerViewAgent(Debugger::DebuggerEngine *engine);
~DisassemblerViewAgent();
void setFrame(const StackFrame &frame, bool tryMixed = true);
void setFrame(const StackFrame &frame, bool tryMixed, bool setMarker);
const StackFrame &frame() const;
void resetLocation();
Q_SLOT void setContents(const QString &contents);
......
......@@ -516,21 +516,15 @@ void DebuggerEngine::resetLocation()
void DebuggerEngine::gotoLocation(const QString &fileName, int lineNumber, bool setMarker)
{
StackFrame frame;
frame.file = fileName;
frame.line = lineNumber;
gotoLocation(frame, setMarker);
debuggerCore()->gotoLocation(fileName, lineNumber, setMarker);
}
void DebuggerEngine::gotoLocation(const StackFrame &frame, bool setMarker)
{
if (debuggerCore()->boolSetting(OperateByInstruction) || !frame.isUsable()) {
if (setMarker)
resetLocation();
d->m_disassemblerViewAgent.setFrame(frame);
} else {
if (debuggerCore()->boolSetting(OperateByInstruction) || !frame.isUsable())
d->m_disassemblerViewAgent.setFrame(frame, true, setMarker);
else
debuggerCore()->gotoLocation(frame.file, frame.line, setMarker);
}
}
// Called from RunControl.
......
......@@ -395,46 +395,13 @@ const char * const SNAPSHOT_KEY = "Ctrl+D,Ctrl+S";
#endif
} // namespace Constants
} // namespace Debugger
static SessionManager *sessionManager()
{
return ProjectExplorerPlugin::instance()->session();
}
static QToolButton *toolButton(QAction *action)
{
QToolButton *button = new QToolButton;
button->setDefaultAction(action);
return button;
}
// Retrieve file name and line and optionally address
// from the data set on the text editor context menu action.
static bool positionFromContextActionData(const QObject *sender,
QString *fileName,
int *lineNumber,
quint64 *address = 0)
{
if (const QAction *action = qobject_cast<const QAction *>(sender)) {
const QVariantList data = action->data().toList();
if (data.size() >= (address ? 3 : 2)) {
*fileName = data.front().toString();
*lineNumber = data.at(1).toInt();
if (address)
*address = data.at(2).toULongLong();
return true;
}
}
return false;
}
namespace Debugger {
namespace Cdb {
void addCdb2OptionPages(QList<Core::IOptionsPage*> *);
} // namespace Cdb
namespace Internal {
// FIXME: Outdated?
......@@ -450,6 +417,34 @@ void addTcfOptionPages(QList<IOptionsPage*> *opts);
void addCdbOptionPages(QList<IOptionsPage*> *opts);
#endif
static SessionManager *sessionManager()
{
return ProjectExplorerPlugin::instance()->session();
}
static QToolButton *toolButton(QAction *action)
{
QToolButton *button = new QToolButton;
button->setDefaultAction(action);
return button;
}
// Retrieve file name and line and optionally address
// from the data set on the text editor context menu action.
static bool positionFromContextActionData(const QObject *sender,
QString *fileName, int *lineNumber, quint64 *address = 0)
{
const QAction *action = qobject_cast<const QAction *>(sender);
QTC_ASSERT(action, return false);
const QVariantList data = action->data().toList();
QTC_ASSERT(data.size() == 3, return false);
*fileName = data.front().toString();
*lineNumber = data.at(1).toInt();
if (address)
*address = data.at(2).toULongLong();
return true;
}
struct AttachRemoteParameters
{
AttachRemoteParameters() : attachPid(0), winCrashEvent(0) {}
......@@ -917,7 +912,7 @@ public slots:
quint64 address;
if (positionFromContextActionData(sender(), &fileName, &lineNumber, &address))
m_breakHandler->toggleBreakpoint(fileName, lineNumber, address);
}
}
void breakpointRemoveMarginActionTriggered()
{
......@@ -2391,7 +2386,9 @@ void DebuggerPluginPrivate::requestContextMenu(TextEditor::ITextEditor *editor,
QString line = editor->contents()
.section('\n', lineNumber - 1, lineNumber - 1);
BreakpointResponse needle;
needle.type = BreakpointByAddress;
needle.address = DisassemblerViewAgent::addressFromDisassemblyLine(line);
address = needle.address;
needle.lineNumber = -1;
id = breakHandler()->findSimilarBreakpoint(needle);
} else {
......
......@@ -1186,8 +1186,9 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
}
// Quickly set the location marker.
if (lineNumber && QFileInfo(fullName).exists())
debuggerCore()->gotoLocation(fullName, lineNumber, true);
if (lineNumber && !debuggerCore()->boolSetting(OperateByInstruction)
&& QFileInfo(fullName).exists())
gotoLocation(fullName, lineNumber, true);
if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state())
......@@ -1443,7 +1444,7 @@ void GdbEngine::handleStop1(const GdbMi &data)
//
// Stack
//
reloadStack(false);
reloadStack(false); // Will trigger register reload.
if (supportsThreads()) {
int currentId = data.findChild("thread-id").data().toInt();
......@@ -1458,11 +1459,6 @@ void GdbEngine::handleStop1(const GdbMi &data)
CB(handleThreadInfo), currentId);
}