Commit 18445dc9 authored by hjk's avatar hjk

debugger: make handling multiple core files a bit more convenient

parent 7c823c6a
......@@ -38,6 +38,8 @@ HEADERS += breakhandler.h \
registerwindow.h \
stackhandler.h \
stackwindow.h \
snapshothandler.h \
snapshotwindow.h \
sourcefileswindow.h \
threadswindow.h \
watchhandler.h \
......@@ -61,6 +63,8 @@ SOURCES += breakhandler.cpp \
procinterrupt.cpp \
registerhandler.cpp \
registerwindow.cpp \
snapshothandler.cpp \
snapshotwindow.cpp \
stackhandler.cpp \
stackwindow.cpp \
sourcefileswindow.cpp \
......
......@@ -6,6 +6,7 @@
<file>images/debugger_breakpoints.png</file>
<file>images/debugger_continue_small.png</file>
<file>images/debugger_interrupt_small.png</file>
<file>images/debugger_snapshot_small.png</file>
<file>images/debugger_start.png</file>
<file>images/debugger_start_small.png</file>
<file>images/debugger_stepinto_small.png</file>
......
......@@ -151,6 +151,7 @@ struct DebuggerManagerActions
QAction *runToFunctionAction;
QAction *jumpToLineAction;
QAction *nextAction;
QAction *snapshotAction;
QAction *watchAction1; // in the Debug menu
QAction *watchAction2; // in the text editor context menu
QAction *breakAction;
......
......@@ -41,15 +41,16 @@
#include "debuggeroutputwindow.h"
#include "moduleswindow.h"
#include "registerwindow.h"
#include "snapshotwindow.h"
#include "stackwindow.h"
#include "sourcefileswindow.h"
#include "threadswindow.h"
#include "watchwindow.h"
#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "snapshothandler.h"
#include "stackhandler.h"
#include "stackframe.h"
#include "watchhandler.h"
......@@ -253,25 +254,28 @@ struct DebuggerManagerPrivate
// FIXME: Remove engine-specific state
DebuggerStartParametersPtr m_startParameters;
qint64 m_inferiorPid;
/// Views
Utils::FancyMainWindow *m_mainWindow;
QLabel *m_statusLabel;
QDockWidget *m_breakDock;
QDockWidget *m_modulesDock;
QDockWidget *m_outputDock;
QDockWidget *m_registerDock;
QDockWidget *m_stackDock;
QDockWidget *m_snapshotDock;
QDockWidget *m_sourceFilesDock;
QDockWidget *m_stackDock;
QDockWidget *m_threadsDock;
QDockWidget *m_watchDock;
BreakHandler *m_breakHandler;
ModulesHandler *m_modulesHandler;
RegisterHandler *m_registerHandler;
SnapshotHandler *m_snapshotHandler;
StackHandler *m_stackHandler;
ThreadsHandler *m_threadsHandler;
WatchHandler *m_watchHandler;
SourceFilesWindow *m_sourceFilesWindow;
DebuggerManagerActions m_actions;
......@@ -279,6 +283,8 @@ struct DebuggerManagerPrivate
QWidget *m_localsWindow;
QWidget *m_registerWindow;
QWidget *m_modulesWindow;
QWidget *m_snapshotWindow;
SourceFilesWindow *m_sourceFilesWindow;
QWidget *m_stackWindow;
QWidget *m_threadsWindow;
QWidget *m_watchersWindow;
......@@ -343,6 +349,7 @@ void DebuggerManager::init()
d->m_modulesWindow = new ModulesWindow(this);
d->m_outputWindow = new DebuggerOutputWindow;
d->m_registerWindow = new RegisterWindow(this);
d->m_snapshotWindow = new SnapshotWindow(this);
d->m_stackWindow = new StackWindow(this);
d->m_sourceFilesWindow = new SourceFilesWindow;
d->m_threadsWindow = new ThreadsWindow;
......@@ -354,6 +361,12 @@ void DebuggerManager::init()
d->m_mainWindow->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
d->m_mainWindow->setDocumentMode(true);
// Snapshots
d->m_snapshotHandler = new SnapshotHandler;
QAbstractItemView *snapshotView =
qobject_cast<QAbstractItemView *>(d->m_snapshotWindow);
snapshotView->setModel(d->m_snapshotHandler);
// Stack
d->m_stackHandler = new StackHandler;
QAbstractItemView *stackView =
......@@ -471,6 +484,9 @@ void DebuggerManager::init()
d->m_actions.watchAction1 = new QAction(tr("Add to Watch Window"), this);
d->m_actions.watchAction2 = new QAction(tr("Add to Watch Window"), this);
d->m_actions.snapshotAction = new QAction(tr("Snapshot"), this);
d->m_actions.snapshotAction->setIcon(QIcon(":/debugger/images/debugger_snapshot_small.png"));
d->m_actions.reverseDirectionAction = new QAction(tr("Reverse Direction"), this);
d->m_actions.reverseDirectionAction->setCheckable(true);
d->m_actions.reverseDirectionAction->setChecked(false);
......@@ -499,6 +515,8 @@ void DebuggerManager::init()
this, SLOT(addToWatchWindow()));
connect(d->m_actions.breakAction, SIGNAL(triggered()),
this, SLOT(toggleBreakpoint()));
connect(d->m_actions.snapshotAction, SIGNAL(triggered()),
this, SLOT(makeSnapshot()));
connect(d->m_statusTimer, SIGNAL(timeout()),
this, SLOT(clearStatusMessage()));
......@@ -525,6 +543,8 @@ void DebuggerManager::init()
d->m_outputDock = d->m_mainWindow->addDockForWidget(d->m_outputWindow);
d->m_snapshotDock = d->m_mainWindow->addDockForWidget(d->m_snapshotWindow);
d->m_stackDock = d->m_mainWindow->addDockForWidget(d->m_stackWindow);
d->m_sourceFilesDock = d->m_mainWindow->addDockForWidget(d->m_sourceFilesWindow);
......@@ -617,6 +637,11 @@ WatchHandler *DebuggerManager::watchHandler() const
return d->m_watchHandler;
}
SnapshotHandler *DebuggerManager::snapshotHandler() const
{
return d->m_snapshotHandler;
}
const CPlusPlus::Snapshot &DebuggerManager::cppCodeModelSnapshot() const
{
if (d->m_codeModelSnapshot.isEmpty() && theDebuggerAction(UseCodeModel)->isChecked())
......@@ -671,6 +696,7 @@ void DebuggerManager::setSimpleDockWidgetArrangement()
d->m_mainWindow->tabifyDockWidget(d->m_watchDock, d->m_registerDock);
d->m_mainWindow->tabifyDockWidget(d->m_watchDock, d->m_threadsDock);
d->m_mainWindow->tabifyDockWidget(d->m_watchDock, d->m_sourceFilesDock);
d->m_mainWindow->tabifyDockWidget(d->m_watchDock, d->m_snapshotDock);
// They following views are rarely used in ordinary debugging. Hiding them
// saves cycles since the corresponding information won't be retrieved.
......@@ -767,11 +793,30 @@ void DebuggerManager::shutdown()
doDelete(d->m_threadsHandler);
doDelete(d->m_modulesHandler);
doDelete(d->m_registerHandler);
doDelete(d->m_snapshotHandler);
doDelete(d->m_stackHandler);
doDelete(d->m_watchHandler);
#undef doDelete
}
void DebuggerManager::makeSnapshot()
{
QTC_ASSERT(d->m_engine, return);
d->m_engine->makeSnapshot();
}
void DebuggerManager::activateSnapshot(int index)
{
QTC_ASSERT(d->m_engine, return);
d->m_engine->activateSnapshot(index);
}
void DebuggerManager::removeSnapshot(int index)
{
QTC_ASSERT(d->m_engine, return);
d->m_snapshotHandler->removeSnapshot(index);
}
BreakpointData *DebuggerManager::findBreakpoint(const QString &fileName, int lineNumber)
{
if (!d->m_breakHandler)
......@@ -1675,6 +1720,7 @@ void DebuggerManager::setState(DebuggerState state, bool forced)
d->m_actions.watchAction1->setEnabled(true);
d->m_actions.watchAction2->setEnabled(true);
d->m_actions.breakAction->setEnabled(true);
d->m_actions.snapshotAction->setEnabled(stopped);
bool interruptIsExit = !running;
if (interruptIsExit) {
......
......@@ -78,6 +78,7 @@ class SourceFilesWindow;
struct StackFrame;
class StackHandler;
class Symbol;
class SnapshotHandler;
class ThreadsHandler;
class WatchData;
class WatchHandler;
......@@ -219,12 +220,15 @@ public slots:
void setBreakpoint(const QString &fileName, int lineNumber);
void activateFrame(int index);
void selectThread(int index);
void activateSnapshot(int index);
void removeSnapshot(int index);
void stepExec();
void stepOutExec();
void nextExec();
void continueExec();
void detachDebugger();
void makeSnapshot();
void addToWatchWindow();
void updateWatchData(const Debugger::Internal::WatchData &data);
......@@ -282,6 +286,7 @@ private:
Internal::StackHandler *stackHandler() const;
Internal::ThreadsHandler *threadsHandler() const;
Internal::WatchHandler *watchHandler() const;
Internal::SnapshotHandler *snapshotHandler() const;
Internal::SourceFilesWindow *sourceFileWindow() const;
QWidget *threadsWindow() const;
......
......@@ -116,6 +116,7 @@ const char * const DETACH = "Debugger.Detach";
const char * const RUN_TO_LINE = "Debugger.RunToLine";
const char * const RUN_TO_FUNCTION = "Debugger.RunToFunction";
const char * const JUMP_TO_LINE = "Debugger.JumpToLine";
const char * const SNAPSHOT = "Debugger.Snapshot";
const char * const TOGGLE_BREAK = "Debugger.ToggleBreak";
const char * const BREAK_BY_FUNCTION = "Debugger.BreakByFunction";
const char * const BREAK_AT_MAIN = "Debugger.BreakAtMain";
......@@ -137,6 +138,7 @@ const char * const TOGGLE_BREAK_KEY = "F8";
const char * const BREAK_BY_FUNCTION_KEY = "Alt+D,Alt+F";
const char * const BREAK_AT_MAIN_KEY = "Alt+D,Alt+M";
const char * const ADD_TO_WATCH_KEY = "Alt+D,Alt+W";
const char * const SNAPSHOT_KEY = "Alt+D,Alt+S";
#else
const char * const INTERRUPT_KEY = "Shift+F5";
const char * const RESET_KEY = "Ctrl+Shift+F5";
......@@ -151,6 +153,7 @@ const char * const TOGGLE_BREAK_KEY = "F9";
const char * const BREAK_BY_FUNCTION_KEY = "";
const char * const BREAK_AT_MAIN_KEY = "";
const char * const ADD_TO_WATCH_KEY = "Ctrl+Alt+Q";
const char * const SNAPSHOT_KEY = "Alt+D,Alt+S";
#endif
} // namespace Constants
......@@ -809,6 +812,11 @@ bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMess
cmd = am->registerAction(sep, QLatin1String("Debugger.Sep.Break"), globalcontext);
mdebug->addAction(cmd);
cmd = am->registerAction(actions.snapshotAction,
Constants::SNAPSHOT, debuggercontext);
cmd->setDefaultKeySequence(QKeySequence(Constants::SNAPSHOT_KEY));
mdebug->addAction(cmd);
cmd = am->registerAction(theDebuggerAction(OperateByInstruction),
Constants::OPERATE_BY_INSTRUCTION, debuggercontext);
mdebug->addAction(cmd);
......@@ -941,6 +949,7 @@ bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMess
debugToolBarLayout->addWidget(toolButton(am->command(Constants::STEP)->action()));
debugToolBarLayout->addWidget(toolButton(am->command(Constants::STEPOUT)->action()));
debugToolBarLayout->addWidget(toolButton(am->command(Constants::OPERATE_BY_INSTRUCTION)->action()));
debugToolBarLayout->addWidget(toolButton(am->command(Constants::SNAPSHOT)->action()));
#ifdef USE_REVERSE_DEBUGGING
debugToolBarLayout->addWidget(new Utils::StyledSeparator);
debugToolBarLayout->addWidget(toolButton(am->command(Constants::REVERSE)->action()));
......
......@@ -53,8 +53,10 @@
#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "snapshothandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
#include "sourcefileswindow.h"
#include "debuggerdialogs.h"
......@@ -65,20 +67,21 @@
#include <projectexplorer/toolchain.h>
#include <coreplugin/icore.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QMetaObject>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtCore/QTemporaryFile>
#include <QtCore/QTextStream>
#include <QtGui/QAction>
#include <QtCore/QCoreApplication>
#include <QtGui/QDialogButtonBox>
#include <QtGui/QLabel>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
#include <QtGui/QDialogButtonBox>
#include <QtGui/QPushButton>
#ifdef Q_OS_UNIX
......@@ -87,13 +90,6 @@
#endif
#include <ctype.h>
#define DIVERT(func, pars) \
do { \
if (hasPython()) \
func ## Python(pars); \
else \
func ## Plain(pars); \
} while (0)
namespace Debugger {
namespace Internal {
......@@ -2699,6 +2695,92 @@ void GdbEngine::handleStackListThreads(const GdbResponse &response)
}
//////////////////////////////////////////////////////////////////////
//
// Snapshot specific stuff
//
//////////////////////////////////////////////////////////////////////
void GdbEngine::makeSnapshot()
{
QString fileName;
QTemporaryFile tf(QDir::tempPath() + _("/gdbsnapshot"));
if (tf.open()) {
fileName = tf.fileName();
tf.close();
postCommand("gcore " + fileName.toLocal8Bit(),
NeedsStop, CB(handleMakeSnapshot), fileName);
} else {
showMessageBox(QMessageBox::Critical, tr("Snapshot Creation Error"),
tr("Cannot create snapshot file."));
}
}
void GdbEngine::handleMakeSnapshot(const GdbResponse &response)
{
if (response.resultClass == GdbResultDone) {
SnapshotData snapshot;
snapshot.setDate(QDateTime::currentDateTime());
snapshot.setLocation(response.cookie.toString());
snapshot.setFrames(manager()->stackHandler()->frames());
manager()->snapshotHandler()->appendSnapshot(snapshot);
} else {
QByteArray msg = response.data.findChild("msg").data();
showMessageBox(QMessageBox::Critical, tr("Snapshot Creation Error"),
tr("Cannot create snapshot:\n") + QString::fromLocal8Bit(msg));
}
}
void GdbEngine::activateSnapshot(int index)
{
SnapshotData snapshot = m_manager->snapshotHandler()->setCurrentIndex(index);
m_startParameters->startMode = AttachCore;
m_startParameters->coreFile = snapshot.location();
if (state() == InferiorUnrunnable) {
// All is well. We are looking at another core file.
setState(EngineShuttingDown);
setState(DebuggerNotReady);
activateSnapshot2();
} else if (state() != DebuggerNotReady) {
QMessageBox *mb = showMessageBox(QMessageBox::Critical,
tr("Snapshot Reloading"),
tr("In order to load snapshots the debugged process needs "
"to be stopped. Continuation will not be possible afterwards.\n"
"Do you want to stop the debugged process and load the selected "
"snapshot?"), QMessageBox::Ok | QMessageBox::Cancel);
if (mb->exec() == QMessageBox::Cancel)
return;
debugMessage(_("KILLING DEBUGGER AS REQUESTED BY USER"));
delete m_gdbAdapter;
m_gdbAdapter = createAdapter(m_startParameters);
postCommand("kill", NeedsStop, CB(handleActivateSnapshot));
} else {
activateSnapshot2();
}
}
void GdbEngine::handleActivateSnapshot(const GdbResponse &response)
{
Q_UNUSED(response);
setState(InferiorShuttingDown);
setState(InferiorShutDown);
setState(EngineShuttingDown);
setState(DebuggerNotReady);
activateSnapshot2();
}
void GdbEngine::activateSnapshot2()
{
// Otherwise the stack data might be stale.
// See http://sourceware.org/bugzilla/show_bug.cgi?id=1124.
setState(EngineStarting);
setState(AdapterStarting);
postCommand("set stack-cache off");
handleAdapterStarted();
}
//////////////////////////////////////////////////////////////////////
//
// Register specific stuff
......
......@@ -348,6 +348,15 @@ private: ////////// View & Data Stuff //////////
bool m_modulesListOutdated;
//
// Snapshot specific stuff
//
virtual void makeSnapshot();
void handleMakeSnapshot(const GdbResponse &response);
void handleActivateSnapshot(const GdbResponse &response);
void activateSnapshot(int index);
void activateSnapshot2();
//
// Register specific stuff
//
......
......@@ -95,6 +95,9 @@ public:
virtual void activateFrame(int index) = 0;
virtual void selectThread(int index) = 0;
virtual void makeSnapshot() {}
virtual void activateSnapshot(int index) { Q_UNUSED(index); }
virtual void attemptBreakpointSynchronization() = 0;
virtual void reloadModules() = 0;
......
/**************************************************************************
**
** 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://qt.nokia.com/contact.
**
**************************************************************************/
#include "snapshothandler.h"
#include "debuggeractions.h"
#include <utils/qtcassert.h>
#include <QtCore/QAbstractTableModel>
#include <QtCore/QDebug>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
namespace Debugger {
namespace Internal {
SnapshotData::SnapshotData()
{}
void SnapshotData::clear()
{
m_frames.clear();
m_location.clear();
m_date = QDateTime();
}
QString SnapshotData::function() const
{
if (m_frames.isEmpty())
return QString();
const StackFrame &frame = m_frames.at(0);
return frame.function + ":" + QString::number(frame.line);
}
QString SnapshotData::toString() const
{
QString res;
QTextStream str(&res);
str << SnapshotHandler::tr("Function:") << ' ' << function() << ' '
<< SnapshotHandler::tr("File:") << ' ' << m_location << ' '
<< SnapshotHandler::tr("Date:") << ' ' << m_date.toString();
return res;
}
QString SnapshotData::toToolTip() const
{
QString res;
QTextStream str(&res);
str << "<html><body><table>"
<< "<tr><td>" << SnapshotHandler::tr("Function:")
<< "</td><td>" << function() << "</td></tr>"
<< "<tr><td>" << SnapshotHandler::tr("File:")
<< "</td><td>" << QDir::toNativeSeparators(m_location) << "</td></tr>"
<< "</table></body></html>";
return res;
}
QDebug operator<<(QDebug d, const SnapshotData &f)
{
QString res;
QTextStream str(&res);
str << f.location();
/*
str << "level=" << f.level << " address=" << f.address;
if (!f.function.isEmpty())
str << ' ' << f.function;
if (!f.location.isEmpty())
str << ' ' << f.location << ':' << f.line;
if (!f.from.isEmpty())
str << " from=" << f.from;
if (!f.to.isEmpty())
str << " to=" << f.to;
*/
d.nospace() << res;
return d;
}
////////////////////////////////////////////////////////////////////////
//
// SnapshotHandler
//
////////////////////////////////////////////////////////////////////////
SnapshotHandler::SnapshotHandler(QObject *parent)
: QAbstractTableModel(parent),
m_positionIcon(QIcon(":/debugger/images/location.svg")),
m_emptyIcon(QIcon(":/debugger/images/empty.svg"))
{
m_currentIndex = 0;
connect(theDebuggerAction(OperateByInstruction), SIGNAL(triggered()),
this, SLOT(resetModel()));
}
SnapshotHandler::~SnapshotHandler()
{
foreach (const SnapshotData &snapshot, m_snapshots)
QFile::remove(snapshot.location());
}
int SnapshotHandler::rowCount(const QModelIndex &parent) const
{
// Since the stack is not a tree, row count is 0 for any valid parent
return parent.isValid() ? 0 : m_snapshots.size();
}
int SnapshotHandler::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : 3;
}
QVariant SnapshotHandler::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() >= m_snapshots.size())
return QVariant();
if (index.row() == m_snapshots.size()) {
if (role == Qt::DisplayRole && index.column() == 0)
return tr("...");
if (role == Qt::DisplayRole && index.column() == 1)
return tr("<More>");
if (role == Qt::DecorationRole && index.column() == 0)
return m_emptyIcon;
return QVariant();
}
const SnapshotData &snapshot = m_snapshots.at(index.row());
if (role == Qt::DisplayRole) {
switch (index.column()) {
case 0: // Function name of topmost snapshot
return snapshot.function();
case 1: // Timestamp
return snapshot.date().toString();
case 2: // File name
return snapshot.location();
}
return QVariant();
}
if (role == Qt::ToolTipRole) {
//: Tooltip for variable
return snapshot.toToolTip();
}
if (role == Qt::DecorationRole && index.column() == 0) {
// Return icon that indicates whether this is the active stack frame
return (index.row() == m_currentIndex) ? m_positionIcon : m_emptyIcon;
}
//if (role == Qt::UserRole)
// return QVariant::fromValue(snapshot);
return QVariant();
}
QVariant SnapshotHandler::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case 0: return tr("Function");
case 1: return tr("Date");
case 2: return tr("Location");
};
}
return QVariant();