Files
qt-creator/src/plugins/debugger/cdb/cdbdebugengine.cpp

1611 lines
54 KiB
C++
Raw Normal View History

/**************************************************************************
2009-02-20 17:07:00 +01:00
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
2009-02-20 17:07:00 +01:00
**
** Contact: Nokia Corporation (qt-info@nokia.com)
2009-02-20 17:07:00 +01:00
**
** Commercial Usage
2009-02-20 17:07:00 +01:00
**
** 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.
2009-02-20 17:07:00 +01:00
**
** GNU Lesser General Public License Usage
2009-02-20 17:07:00 +01:00
**
** 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.
2009-02-20 17:07:00 +01:00
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://www.qtsoftware.com/contact.
2009-02-20 17:07:00 +01:00
**
**************************************************************************/
2009-02-20 17:07:00 +01:00
2009-02-09 13:07:38 +01:00
#include "cdbdebugengine.h"
2009-02-20 17:07:00 +01:00
#include "cdbdebugengine_p.h"
#include "cdbstacktracecontext.h"
#include "cdbstackframecontext.h"
#include "cdbsymbolgroupcontext.h"
#include "cdbbreakpoint.h"
#include "cdbmodules.h"
#include "cdbassembler.h"
#include "cdboptionspage.h"
#include "cdboptions.h"
2009-02-09 11:35:43 +01:00
#include "debuggeractions.h"
2009-02-09 11:35:43 +01:00
#include "debuggermanager.h"
#include "breakhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
#include "registerhandler.h"
#include "moduleshandler.h"
#include "disassemblerhandler.h"
#include "watchutils.h"
2009-02-09 11:35:43 +01:00
#include <coreplugin/icore.h>
2009-02-17 11:16:23 +01:00
#include <utils/qtcassert.h>
2009-03-04 16:00:43 +01:00
#include <utils/winutils.h>
#include <utils/consoleprocess.h>
#include <texteditor/itexteditor.h>
2009-02-17 11:16:23 +01:00
2009-02-23 14:46:46 +01:00
#include <QtCore/QDebug>
#include <QtCore/QTimer>
2009-02-23 14:46:46 +01:00
#include <QtCore/QTimerEvent>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
2009-02-23 16:13:35 +01:00
#include <QtCore/QLibrary>
#include <QtCore/QCoreApplication>
#include <QtGui/QMessageBox>
#include <QtGui/QMainWindow>
#include <QtGui/QApplication>
#include <QtGui/QToolTip>
2009-02-09 11:35:43 +01:00
#define DBGHELP_TRANSLATE_TCHAR
2009-02-20 14:56:36 +01:00
#include <inc/Dbghelp.h>
2009-02-09 11:35:43 +01:00
static const char *dbgHelpDllC = "dbghelp";
2009-02-23 16:13:35 +01:00
static const char *dbgEngineDllC = "dbgeng";
static const char *debugCreateFuncC = "DebugCreate";
2009-02-09 11:35:43 +01:00
static const char *localSymbolRootC = "local";
namespace Debugger {
namespace Internal {
typedef QList<WatchData> WatchList;
// ----- Message helpers
QString msgDebugEngineComResult(HRESULT hr)
2009-03-04 16:00:43 +01:00
{
switch (hr) {
case S_OK:
return QLatin1String("S_OK");
case S_FALSE:
return QLatin1String("S_FALSE");
2009-03-04 16:00:43 +01:00
case E_FAIL:
break;
case E_INVALIDARG:
return QLatin1String("E_INVALIDARG");
case E_NOINTERFACE:
return QLatin1String("E_NOINTERFACE");
case E_OUTOFMEMORY:
return QLatin1String("E_OUTOFMEMORY");
case E_UNEXPECTED:
return QLatin1String("E_UNEXPECTED");
case E_NOTIMPL:
return QLatin1String("E_NOTIMPL");
2009-03-04 16:00:43 +01:00
}
if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED))
return QLatin1String("ERROR_ACCESS_DENIED");;
if (hr == HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT))
return QLatin1String("STATUS_CONTROL_C_EXIT");
return QLatin1String("E_FAIL ") + Core::Utils::winErrorMessage(HRESULT_CODE(hr));
2009-03-04 16:00:43 +01:00
}
static QString msgStackIndexOutOfRange(int idx, int size)
{
return QString::fromLatin1("Frame index %1 out of range (%2).").arg(idx).arg(size);
}
QString msgComFailed(const char *func, HRESULT hr)
{
return QString::fromLatin1("%1 failed: %2").arg(QLatin1String(func), msgDebugEngineComResult(hr));
}
static const char *msgNoStackTraceC = "Internal error: no stack trace present.";
2009-02-23 16:13:35 +01:00
static inline QString msgLibLoadFailed(const QString &lib, const QString &why)
{
return CdbDebugEngine::tr("Unable to load the debugger engine library '%1': %2").
arg(lib, why);
}
// Format function failure message. Pass in Q_FUNC_INFO
static QString msgFunctionFailed(const char *func, const QString &why)
{
// Strip a "cdecl_ int namespace1::class::foo(int bar)" as
// returned by Q_FUNC_INFO down to "foo"
QString function = QLatin1String(func);
const int firstParentPos = function.indexOf(QLatin1Char('('));
if (firstParentPos != -1)
function.truncate(firstParentPos);
const int classSepPos = function.lastIndexOf(QLatin1String("::"));
if (classSepPos != -1)
function.remove(0, classSepPos + 2);
//: Function call failed
return CdbDebugEngine::tr("The function \"%1()\" failed: %2").arg(function, why);
}
// ----- Engine helpers
static inline ULONG getInterruptTimeOutSecs(CIDebugControl *ctl)
{
ULONG rc = 0;
ctl->GetInterruptTimeout(&rc);
return rc;
}
bool getExecutionStatus(CIDebugControl *ctl,
ULONG *executionStatus,
QString *errorMessage /* = 0 */)
{
const HRESULT hr = ctl->GetExecutionStatus(executionStatus);
if (FAILED(hr)) {
if (errorMessage)
*errorMessage = msgComFailed("GetExecutionStatus", hr);
return false;
}
return true;
}
const char *executionStatusString(ULONG executionStatus)
{
switch (executionStatus) {
case DEBUG_STATUS_NO_CHANGE:
return "DEBUG_STATUS_NO_CHANGE";
case DEBUG_STATUS_GO:
return "DEBUG_STATUS_GO";
case DEBUG_STATUS_GO_HANDLED:
return "DEBUG_STATUS_GO_HANDLED";
case DEBUG_STATUS_GO_NOT_HANDLED:
return "DEBUG_STATUS_GO_NOT_HANDLED";
case DEBUG_STATUS_STEP_OVER:
return "DEBUG_STATUS_STEP_OVER";
case DEBUG_STATUS_STEP_INTO:
return "DEBUG_STATUS_STEP_INTO";
case DEBUG_STATUS_BREAK:
return "DEBUG_STATUS_BREAK";
case DEBUG_STATUS_NO_DEBUGGEE:
return "DEBUG_STATUS_NO_DEBUGGEE";
case DEBUG_STATUS_STEP_BRANCH:
return "DEBUG_STATUS_STEP_BRANCH";
case DEBUG_STATUS_IGNORE_EVENT:
return "DEBUG_STATUS_IGNORE_EVENT";
case DEBUG_STATUS_RESTART_REQUESTED:
return "DEBUG_STATUS_RESTART_REQUESTED";
case DEBUG_STATUS_REVERSE_GO:
return "DEBUG_STATUS_REVERSE_GO";
case DEBUG_STATUS_REVERSE_STEP_BRANCH:
return "DEBUG_STATUS_REVERSE_STEP_BRANCH";
case DEBUG_STATUS_REVERSE_STEP_OVER:
return "DEBUG_STATUS_REVERSE_STEP_OVER";
case DEBUG_STATUS_REVERSE_STEP_INTO:
return "DEBUG_STATUS_REVERSE_STEP_INTO";
default:
break;
}
return "<Unknown execution status>";
}
// Debug convenience
const char *executionStatusString(CIDebugControl *ctl)
{
ULONG executionStatus;
if (getExecutionStatus(ctl, &executionStatus))
return executionStatusString(executionStatus);
return "<failed>";
}
// --------- DebuggerEngineLibrary
2009-02-23 16:13:35 +01:00
DebuggerEngineLibrary::DebuggerEngineLibrary() :
m_debugCreate(0)
{
}
// Build a lib name as "Path\x.dll"
static inline QString libPath(const QString &libName, const QString &path = QString())
2009-02-23 16:13:35 +01:00
{
QString rc = path;
if (!rc.isEmpty())
rc += QDir::separator();
rc += libName;
rc += QLatin1String(".dll");
return rc;
}
2009-02-23 16:13:35 +01:00
bool DebuggerEngineLibrary::init(const QString &path, QString *errorMessage)
{
// Load the dependent help lib first
const QString helpLibPath = libPath(QLatin1String(dbgHelpDllC), path);
QLibrary helpLib(helpLibPath, 0);
if (!helpLib.isLoaded() && !helpLib.load()) {
*errorMessage = msgLibLoadFailed(helpLibPath, helpLib.errorString());
return false;
}
// Load dbgeng lib
const QString engineLibPath = libPath(QLatin1String(dbgEngineDllC), path);
QLibrary lib(engineLibPath, 0);
2009-02-23 16:13:35 +01:00
if (!lib.isLoaded() && !lib.load()) {
*errorMessage = msgLibLoadFailed(engineLibPath, lib.errorString());
2009-02-23 16:13:35 +01:00
return false;
}
// Locate symbols
void *createFunc = lib.resolve(debugCreateFuncC);
if (!createFunc) {
*errorMessage = CdbDebugEngine::tr("Unable to resolve '%1' in the debugger engine library '%2'").
arg(QLatin1String(debugCreateFuncC), QLatin1String(dbgEngineDllC));
return false;
}
m_debugCreate = static_cast<DebugCreateFunction>(createFunc);
return true;
}
// ----- SyntaxSetter
SyntaxSetter::SyntaxSetter(CIDebugControl *ctl, ULONG desiredSyntax) :
m_desiredSyntax(desiredSyntax),
m_ctl(ctl)
{
m_ctl->GetExpressionSyntax(&m_oldSyntax);
if (m_oldSyntax != m_desiredSyntax)
m_ctl->SetExpressionSyntax(m_desiredSyntax);
}
SyntaxSetter::~SyntaxSetter()
{
if (m_oldSyntax != m_desiredSyntax)
m_ctl->SetExpressionSyntax(m_oldSyntax);
}
// CdbComInterfaces
CdbComInterfaces::CdbComInterfaces() :
debugClient(0),
debugControl(0),
debugSystemObjects(0),
debugSymbols(0),
debugRegisters(0),
debugDataSpaces(0)
{
}
2009-02-23 16:13:35 +01:00
// --- CdbDebugEnginePrivate
CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent,
const QSharedPointer<CdbOptions> &options,
CdbDebugEngine* engine) :
m_options(options),
2009-02-09 11:35:43 +01:00
m_hDebuggeeProcess(0),
m_hDebuggeeThread(0),
m_breakEventMode(BreakEventHandle),
m_dumper(new CdbDumperHelper(parent, &m_cif)),
2009-02-09 11:35:43 +01:00
m_watchTimer(-1),
2009-02-20 17:07:00 +01:00
m_debugEventCallBack(engine),
2009-02-23 14:46:46 +01:00
m_engine(engine),
m_debuggerManager(parent),
m_debuggerManagerAccess(parent->engineInterface()),
m_currentStackTrace(0),
2009-03-27 17:19:39 +01:00
m_firstActivatedFrame(true),
m_mode(AttachCore)
{
}
bool CdbDebugEnginePrivate::init(QString *errorMessage)
2009-02-09 11:35:43 +01:00
{
// Load the DLL
DebuggerEngineLibrary lib;
if (!lib.init(m_options->path, errorMessage))
return false;
// Initialize the COM interfaces
2009-02-09 11:35:43 +01:00
HRESULT hr;
hr = lib.debugCreate( __uuidof(IDebugClient5), reinterpret_cast<void**>(&m_cif.debugClient));
2009-02-23 16:13:35 +01:00
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugClient5 failed: %1").arg(msgDebugEngineComResult(hr));
return false;
2009-02-23 16:13:35 +01:00
}
m_cif.debugClient->SetOutputCallbacksWide(&m_debugOutputCallBack);
m_cif.debugClient->SetEventCallbacksWide(&m_debugEventCallBack);
hr = lib.debugCreate( __uuidof(IDebugControl4), reinterpret_cast<void**>(&m_cif.debugControl));
2009-02-23 16:13:35 +01:00
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugControl4 failed: %1").arg(msgDebugEngineComResult(hr));
return false;
2009-02-23 16:13:35 +01:00
}
m_cif.debugControl->SetCodeLevel(DEBUG_LEVEL_SOURCE);
hr = lib.debugCreate( __uuidof(IDebugSystemObjects4), reinterpret_cast<void**>(&m_cif.debugSystemObjects));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugSystemObjects4 failed: %1").arg(msgDebugEngineComResult(hr));
return false;
}
2009-02-23 16:13:35 +01:00
hr = lib.debugCreate( __uuidof(IDebugSymbols3), reinterpret_cast<void**>(&m_cif.debugSymbols));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugSymbols3 failed: %1").arg(msgDebugEngineComResult(hr));
return false;
}
2009-02-23 16:13:35 +01:00
hr = lib.debugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_cif.debugRegisters));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugRegisters2 failed: %1").arg(msgDebugEngineComResult(hr));
return false;
}
hr = lib.debugCreate( __uuidof(IDebugDataSpaces4), reinterpret_cast<void**>(&m_cif.debugDataSpaces));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugDataSpaces4 failed: %1").arg(msgDebugEngineComResult(hr));
return false;
}
if (debugCDB)
qDebug() << QString::fromLatin1("CDB Initialization succeeded, interrupt time out %1s.").arg(getInterruptTimeOutSecs(m_cif.debugControl));
return true;
2009-02-23 16:13:35 +01:00
}
2009-02-09 11:35:43 +01:00
IDebuggerEngine *CdbDebugEngine::create(DebuggerManager *parent,
const QSharedPointer<CdbOptions> &options,
QString *errorMessage)
2009-02-23 16:13:35 +01:00
{
CdbDebugEngine *rc = new CdbDebugEngine(parent, options);
if (rc->m_d->init(errorMessage)) {
rc->syncDebuggerPaths();
return rc;
}
delete rc;
return 0;
2009-02-09 11:35:43 +01:00
}
2009-02-20 17:07:00 +01:00
CdbDebugEnginePrivate::~CdbDebugEnginePrivate()
2009-02-09 11:35:43 +01:00
{
cleanStackTrace();
if (m_cif.debugClient)
m_cif.debugClient->Release();
if (m_cif.debugControl)
m_cif.debugControl->Release();
if (m_cif.debugSystemObjects)
m_cif.debugSystemObjects->Release();
if (m_cif.debugSymbols)
m_cif.debugSymbols->Release();
if (m_cif.debugRegisters)
m_cif.debugRegisters->Release();
if (m_cif.debugDataSpaces)
m_cif.debugDataSpaces->Release();
2009-02-09 11:35:43 +01:00
}
void CdbDebugEnginePrivate::clearForRun()
{
if (debugCDB)
qDebug() << Q_FUNC_INFO;
m_breakEventMode = BreakEventHandle;
m_firstActivatedFrame = false;
cleanStackTrace();
m_editorToolTipCache.clear();
}
void CdbDebugEnginePrivate::cleanStackTrace()
{
if (m_currentStackTrace) {
delete m_currentStackTrace;
m_currentStackTrace = 0;
}
}
CdbDebugEngine::CdbDebugEngine(DebuggerManager *parent, const QSharedPointer<CdbOptions> &options) :
2009-02-23 16:13:35 +01:00
IDebuggerEngine(parent),
m_d(new CdbDebugEnginePrivate(parent, options, this))
2009-02-20 17:07:00 +01:00
{
m_d->m_consoleStubProc.setMode(Core::Utils::ConsoleProcess::Suspend);
connect(&m_d->m_consoleStubProc, SIGNAL(processError(QString)), this, SLOT(slotConsoleStubError(QString)));
connect(&m_d->m_consoleStubProc, SIGNAL(processStarted()), this, SLOT(slotConsoleStubStarted()));
connect(&m_d->m_consoleStubProc, SIGNAL(wrapperStopped()), this, SLOT(slotConsoleStubTerminated()));
connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggerOutput(QString,QString)),
m_d->m_debuggerManager, SLOT(showDebuggerOutput(QString,QString)));
connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggerInputPrompt(QString,QString)),
m_d->m_debuggerManager, SLOT(showDebuggerInput(QString,QString)));
connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggeeOutput(QString)),
m_d->m_debuggerManager, SLOT(showApplicationOutput(QString)));
connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggeeInputPrompt(QString)),
m_d->m_debuggerManager, SLOT(showApplicationOutput(QString)));
2009-02-20 17:07:00 +01:00
}
CdbDebugEngine::~CdbDebugEngine()
{
delete m_d;
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::startWatchTimer()
2009-02-09 11:35:43 +01:00
{
if (debugCDB)
qDebug() << Q_FUNC_INFO;
2009-02-20 17:07:00 +01:00
if (m_d->m_watchTimer == -1)
m_d->m_watchTimer = startTimer(0);
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::killWatchTimer()
2009-02-09 11:35:43 +01:00
{
if (debugCDB)
qDebug() << Q_FUNC_INFO;
2009-02-20 17:07:00 +01:00
if (m_d->m_watchTimer != -1) {
killTimer(m_d->m_watchTimer);
m_d->m_watchTimer = -1;
2009-02-09 11:35:43 +01:00
}
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::shutdown()
2009-02-09 11:35:43 +01:00
{
exitDebugger();
}
QString CdbDebugEngine::editorToolTip(const QString &exp, const QString &function)
2009-02-09 11:35:43 +01:00
{
// Figure the editor tooltip. Ask the frame context of the
// function if it is a local variable it knows. If that is not
// the case, try to evaluate via debugger
QString errorMessage;
QString rc;
// Find the frame of the function if there is any
CdbStackFrameContext *frame = 0;
if (m_d->m_currentStackTrace && !function.isEmpty()) {
const int frameIndex = m_d->m_currentStackTrace->indexOf(function);
if (frameIndex != -1)
frame = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
}
if (frame && frame->editorToolTip(QLatin1String("local.") + exp, &rc, &errorMessage))
return rc;
// No function/symbol context found, try to evaluate in current context.
// Do not append type as this will mostly be 'long long' for integers, etc.
QString type;
if (!evaluateExpression(exp, &rc, &type, &errorMessage))
return QString();
return rc;
}
void CdbDebugEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
{
typedef CdbDebugEnginePrivate::EditorToolTipCache EditorToolTipCache;
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << cursorPos;
// Need a stopped debuggee and a cpp file
if (!m_d->m_hDebuggeeProcess || m_d->isDebuggeeRunning())
return;
if (!isCppEditor(editor))
return;
// Determine expression and function
QString toolTip;
do {
int line;
int column;
QString function;
const QString exp = cppExpressionAt(editor, cursorPos, &line, &column, &function);
if (function.isEmpty() || exp.isEmpty())
break;
// Check cache (key containing function) or try to figure out expression
QString cacheKey = function;
cacheKey += QLatin1Char('@');
cacheKey += exp;
const EditorToolTipCache::const_iterator cit = m_d->m_editorToolTipCache.constFind(cacheKey);
if (cit != m_d->m_editorToolTipCache.constEnd()) {
toolTip = cit.value();
} else {
toolTip = editorToolTip(exp, function);
if (!toolTip.isEmpty())
m_d->m_editorToolTipCache.insert(cacheKey, toolTip);
}
} while (false);
// Display
QToolTip::hideText();
if (!toolTip.isEmpty())
QToolTip::showText(mousePos, toolTip);
2009-02-09 11:35:43 +01:00
}
void CdbDebugEnginePrivate::clearDisplay()
{
m_debuggerManagerAccess->threadsHandler()->removeAll();
m_debuggerManagerAccess->modulesHandler()->removeAll();
m_debuggerManagerAccess->registerHandler()->removeAll();
}
bool CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> &sp)
{
m_d->clearDisplay();
const DebuggerStartMode mode = m_d->m_debuggerManager->startMode();
// Figure out dumper. @TODO: same in gdb...
const QString dumperLibName = QDir::toNativeSeparators(m_d->m_debuggerManagerAccess->qtDumperLibraryName());
bool dumperEnabled = mode != AttachCore
&& mode != AttachCrashedExternal
&& !dumperLibName.isEmpty()
&& m_d->m_debuggerManagerAccess->qtDumperLibraryEnabled();
if (dumperEnabled) {
const QFileInfo fi(dumperLibName);
if (!fi.isFile()) {
const QString msg = tr("The dumper library '%1' does not exist.").arg(dumperLibName);
m_d->m_debuggerManagerAccess->showQtDumperLibraryWarning(msg);
dumperEnabled = false;
}
}
m_d->m_dumper->reset(dumperLibName, dumperEnabled);
2009-02-23 14:46:46 +01:00
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
2009-03-04 16:00:43 +01:00
QString errorMessage;
bool rc = false;
bool needWatchTimer = false;
m_d->clearForRun();
switch (mode) {
2009-03-04 16:00:43 +01:00
case AttachExternal:
case AttachCrashedExternal:
rc = startAttachDebugger(sp->attachPID, mode, &errorMessage);
needWatchTimer = true; // Fetch away module load, etc. even if crashed
2009-03-04 16:00:43 +01:00
break;
case StartInternal:
case StartExternal:
if (sp->useTerminal) {
// Launch console stub and wait for its startup
m_d->m_consoleStubProc.stop(); // We leave the console open, so recycle it now.
m_d->m_consoleStubProc.setWorkingDirectory(sp->workingDir);
m_d->m_consoleStubProc.setEnvironment(sp->environment);
rc = m_d->m_consoleStubProc.start(sp->executable, sp->processArgs);
if (!rc)
errorMessage = tr("The console stub process was unable to start '%1'.").arg(sp->executable);
// continues in slotConsoleStubStarted()...
} else {
needWatchTimer = true;
rc = startDebuggerWithExecutable(mode, &errorMessage);
}
2009-03-04 16:00:43 +01:00
break;
case AttachCore:
2009-05-07 15:34:52 +02:00
errorMessage = tr("Attaching to core files is not supported!");
2009-03-04 16:00:43 +01:00
break;
}
if (rc) {
2009-05-07 15:34:52 +02:00
m_d->m_debuggerManager->showStatusMessage(tr("Debugger running"), -1);
if (needWatchTimer)
startWatchTimer();
2009-03-04 16:00:43 +01:00
} else {
warning(errorMessage);
2009-03-04 16:00:43 +01:00
}
return rc;
}
2009-02-09 11:35:43 +01:00
bool CdbDebugEngine::startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage)
2009-03-04 16:00:43 +01:00
{
// Need to attrach invasively, otherwise, no notification signals
// for for CreateProcess/ExitProcess occur.
const ULONG flags = DEBUG_ATTACH_INVASIVE_RESUME_PROCESS;
const HRESULT hr = m_d->m_cif.debugClient->AttachProcess(NULL, pid, flags);
2009-03-04 16:00:43 +01:00
if (debugCDB)
qDebug() << "Attaching to " << pid << " returns " << hr << executionStatusString(m_d->m_cif.debugControl);
2009-03-04 16:00:43 +01:00
if (FAILED(hr)) {
2009-05-07 15:34:52 +02:00
*errorMessage = tr("Attaching to a process failed for process id %1: %2").arg(pid).arg(msgDebugEngineComResult(hr));
2009-03-04 16:00:43 +01:00
return false;
} else {
m_d->m_mode = sm;
2009-03-04 16:00:43 +01:00
}
return true;
}
bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString *errorMessage)
2009-03-04 16:00:43 +01:00
{
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
2009-02-09 11:35:43 +01:00
DEBUG_CREATE_PROCESS_OPTIONS dbgopts;
memset(&dbgopts, 0, sizeof(dbgopts));
dbgopts.CreateFlags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS;
const QSharedPointer<DebuggerStartParameters> sp = m_d->m_debuggerManager->startParameters();
const QString filename(sp->executable);
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO <<filename;
2009-02-09 11:35:43 +01:00
2009-02-23 14:46:46 +01:00
const QFileInfo fi(filename);
m_d->m_cif.debugSymbols->AppendImagePathWide(reinterpret_cast<PCWSTR>(QDir::toNativeSeparators(fi.absolutePath()).utf16()));
//m_cif.debugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS);
m_d->m_cif.debugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS);
//m_cif.debugSymbols->AddSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS | SYMOPT_NO_IMAGE_SEARCH);
2009-02-09 11:35:43 +01:00
2009-03-04 16:00:43 +01:00
// TODO console
const QString cmd = Core::Utils::AbstractProcess::createWinCommandline(filename, sp->processArgs);
2009-03-04 16:00:43 +01:00
if (debugCDB)
qDebug() << "Starting " << cmd;
PCWSTR env = 0;
QByteArray envData;
if (!sp->environment.empty()) {
envData = Core::Utils::AbstractProcess::createWinEnvironment(Core::Utils::AbstractProcess::fixWinEnvironment(sp->environment));
2009-03-04 16:00:43 +01:00
env = reinterpret_cast<PCWSTR>(envData.data());
}
const HRESULT hr = m_d->m_cif.debugClient->CreateProcess2Wide(NULL,
reinterpret_cast<PWSTR>(const_cast<ushort *>(cmd.utf16())),
2009-03-04 16:00:43 +01:00
&dbgopts,
sizeof(dbgopts),
reinterpret_cast<PCWSTR>(sp->workingDir.utf16()),
2009-03-04 16:00:43 +01:00
env);
if (FAILED(hr)) {
2009-05-07 15:34:52 +02:00
*errorMessage = tr("Unable to create a process '%1': %2").arg(cmd, msgDebugEngineComResult(hr));
2009-03-04 16:00:43 +01:00
m_d->m_debuggerManagerAccess->notifyInferiorExited();
2009-02-23 14:46:46 +01:00
return false;
} else {
m_d->m_mode = sm;
2009-02-09 11:35:43 +01:00
}
2009-03-04 16:00:43 +01:00
m_d->m_debuggerManagerAccess->notifyInferiorRunning();
2009-02-09 11:35:43 +01:00
return true;
}
void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle)
{
setDebuggeeHandles(reinterpret_cast<HANDLE>(processHandle), reinterpret_cast<HANDLE>(initialThreadHandle));
m_debuggerManagerAccess->notifyInferiorRunning();
ULONG currentThreadId;
if (SUCCEEDED(m_cif.debugSystemObjects->GetThreadIdByHandle(initialThreadHandle, &currentThreadId))) {
m_currentThreadId = currentThreadId;
} else {
m_currentThreadId = 0;
}
// Clear any saved breakpoints and set initial breakpoints
m_engine->executeDebuggerCommand(QLatin1String("bc"));
if (m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints())
m_engine->attemptBreakpointSynchronization();
// Attaching to crashed: This handshake (signalling an event) is required for
// the exception to be delivered to the debugger
if (m_mode == AttachCrashedExternal) {
const QString crashParameter = m_debuggerManager->startParameters()->crashParameter;
if (!crashParameter.isEmpty()) {
ULONG64 evtNr = crashParameter.toULongLong();
const HRESULT hr = m_cif.debugControl->SetNotifyEventHandle(evtNr);
if (FAILED(hr))
m_engine->warning(QString::fromLatin1("Handshake failed on event #%1: %2").arg(evtNr).arg(msgComFailed("SetNotifyEventHandle", hr)));
}
}
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << executionStatusString(m_cif.debugControl);
}
void CdbDebugEngine::processTerminated(unsigned long exitCode)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << exitCode;
m_d->clearForRun();
m_d->setDebuggeeHandles(0, 0);
m_d->m_debuggerManagerAccess->notifyInferiorExited();
m_d->m_debuggerManager->exitDebugger();
}
2009-05-11 16:54:35 +02:00
// End debugging using
void CdbDebugEnginePrivate::endDebugging(EndDebuggingMode em)
2009-02-09 11:35:43 +01:00
{
2009-05-11 16:54:35 +02:00
enum Action { Detach, Terminate };
2009-02-23 14:46:46 +01:00
if (debugCDB)
2009-05-11 16:54:35 +02:00
qDebug() << Q_FUNC_INFO << em;
if (m_mode == AttachCore || !m_hDebuggeeProcess)
return;
// Figure out action
Action action;
switch (em) {
case EndDebuggingAuto:
action = (m_mode == AttachExternal || m_mode == AttachCrashedExternal) ?
Detach : Terminate;
2009-05-11 16:54:35 +02:00
break;
case EndDebuggingDetach:
action = Detach;
break;
case EndDebuggingTerminate:
action = Terminate;
break;
}
if (debugCDB)
qDebug() << Q_FUNC_INFO << action;
// Need a stopped debuggee to act
QString errorMessage;
const bool wasRunning = isDebuggeeRunning();
if (wasRunning) { // Process must be stopped in order to terminate
interruptInterferiorProcess(&errorMessage);
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
2009-05-11 16:54:35 +02:00
HRESULT hr;
switch (action) {
case Detach:
hr = m_cif.debugClient->DetachCurrentProcess();
if (FAILED(hr))
errorMessage += msgComFailed("DetachCurrentProcess", hr);
break;
case Terminate:
hr = m_cif.debugClient->TerminateCurrentProcess();
if (FAILED(hr))
errorMessage += msgComFailed("TerminateCurrentProcess", hr);
if (!wasRunning) {
hr = m_cif.debugClient->TerminateProcesses();
if (FAILED(hr))
2009-05-11 16:54:35 +02:00
errorMessage += msgComFailed("TerminateProcesses", hr);
}
2009-05-11 16:54:35 +02:00
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
break;
}
setDebuggeeHandles(0, 0);
m_engine->killWatchTimer();
// Clean up resources (open files, etc.)
hr = m_cif.debugClient->EndSession(DEBUG_END_PASSIVE);
if (FAILED(hr))
errorMessage += msgComFailed("EndSession", hr);
2009-05-11 16:54:35 +02:00
if (!errorMessage.isEmpty()) {
errorMessage = QString::fromLatin1("There were errors trying to end debugging: %1").arg(errorMessage);
m_debuggerManagerAccess->showDebuggerOutput(QLatin1String("error"), errorMessage);
m_engine->warning(errorMessage);
}
2009-05-11 16:54:35 +02:00
}
void CdbDebugEngine::exitDebugger()
{
m_d->endDebugging();
}
void CdbDebugEngine::detachDebugger()
{
m_d->endDebugging(CdbDebugEnginePrivate::EndDebuggingDetach);
2009-02-09 11:35:43 +01:00
}
CdbStackFrameContext *CdbDebugEnginePrivate::getStackFrameContext(int frameIndex, QString *errorMessage) const
{
2009-03-27 17:19:39 +01:00
if (!m_currentStackTrace) {
*errorMessage = QLatin1String(msgNoStackTraceC);
return 0;
}
if (CdbStackFrameContext *sg = m_currentStackTrace->frameContextAt(frameIndex, errorMessage))
2009-03-27 17:19:39 +01:00
return sg;
return 0;
}
void CdbDebugEngine::evaluateWatcher(WatchData *wd)
{
if (debugCDBWatchHandling)
qDebug() << Q_FUNC_INFO << wd->exp;
QString errorMessage;
QString value;
QString type;
if (evaluateExpression(wd->exp, &value, &type, &errorMessage)) {
wd->setValue(value);
wd->setType(type);
} else {
wd->setValue(errorMessage);
wd->setTypeUnneeded();
}
wd->setChildCount(0);
}
2009-06-17 17:34:48 +02:00
void CdbDebugEngine::updateWatchData(const WatchData &incomplete)
2009-02-09 11:35:43 +01:00
{
// Watch item was edited while running
if (m_d->isDebuggeeRunning())
return;
if (debugCDBWatchHandling)
qDebug() << Q_FUNC_INFO << "\n " << incomplete.toString();
2009-06-17 17:34:48 +02:00
WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
if (incomplete.iname.startsWith(QLatin1String("watch."))) {
WatchData watchData = incomplete;
evaluateWatcher(&watchData);
watchHandler->insertData(watchData);
return;
}
2009-03-27 17:19:39 +01:00
const int frameIndex = m_d->m_debuggerManagerAccess->stackHandler()->currentIndex();
2009-03-27 17:19:39 +01:00
bool success = false;
QString errorMessage;
do {
2009-06-17 17:34:48 +02:00
CdbStackFrameContext *sg = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
if (!sg)
break;
if (!sg->completeData(incomplete, watchHandler, &errorMessage))
break;
success = true;
} while (false);
2009-03-27 17:19:39 +01:00
if (!success)
warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::stepExec()
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO;
m_d->clearForRun();
const HRESULT hr = m_d->m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_STEP_INTO);
if (FAILED(hr))
warning(msgFunctionFailed(Q_FUNC_INFO, msgComFailed("SetExecutionStatus", hr)));
m_d->m_breakEventMode = CdbDebugEnginePrivate::BreakEventIgnoreOnce;
2009-02-09 11:35:43 +01:00
startWatchTimer();
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::stepOutExec()
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO;
StackHandler* sh = m_d->m_debuggerManagerAccess->stackHandler();
2009-02-09 11:35:43 +01:00
const int idx = sh->currentIndex() + 1;
QList<StackFrame> stackframes = sh->frames();
if (idx < 0 || idx >= stackframes.size()) {
warning(QString::fromLatin1("cannot step out"));
2009-02-09 11:35:43 +01:00
return;
}
// Set a temporary breakpoint and continue
2009-02-09 11:35:43 +01:00
const StackFrame& frame = stackframes.at(idx);
bool success = false;
QString errorMessage;
do {
const ULONG64 address = frame.address.toULongLong(&success, 16);
if (!success) {
errorMessage = QLatin1String("Cannot obtain address from stack frame");
break;
}
2009-02-09 11:35:43 +01:00
IDebugBreakpoint2* pBP;
HRESULT hr = m_d->m_cif.debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &pBP);
if (FAILED(hr) || !pBP) {
errorMessage = QString::fromLatin1("Cannot create temporary breakpoint: %1").arg(msgDebugEngineComResult(hr));
break;
}
2009-02-09 11:35:43 +01:00
pBP->SetOffset(address);
pBP->AddFlags(DEBUG_BREAKPOINT_ENABLED);
pBP->AddFlags(DEBUG_BREAKPOINT_ONE_SHOT);
if (!m_d->continueInferior(&errorMessage))
break;
success = true;
} while (false);
if (!success)
warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::nextExec()
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO;
m_d->clearForRun();
const HRESULT hr = m_d->m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_STEP_OVER);
if (SUCCEEDED(hr)) {
startWatchTimer();
} else {
warning(msgFunctionFailed(Q_FUNC_INFO, msgComFailed("SetExecutionStatus", hr)));
}
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::stepIExec()
2009-02-09 11:35:43 +01:00
{
warning(QString::fromLatin1("CdbDebugEngine::stepIExec() not implemented"));
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::nextIExec()
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO;
m_d->clearForRun();
const HRESULT hr = m_d->m_cif.debugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, "p", 0);
if (SUCCEEDED(hr)) {
startWatchTimer();
} else {
warning(msgFunctionFailed(Q_FUNC_INFO, msgDebugEngineComResult(hr)));
}
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::continueInferior()
{
QString errorMessage;
if (!m_d->continueInferior(&errorMessage))
warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
}
// Continue process without notifications
bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessagePtr /* = 0 */)
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO;
const HRESULT hr = m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_GO);
if (FAILED(hr)) {
const QString errorMessage = msgComFailed("SetExecutionStatus", hr);
if (errorMessagePtr) {
*errorMessagePtr = errorMessage;
} else {
m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
}
return false;
}
return true;
}
2009-02-23 14:46:46 +01:00
// Continue process with notifications
bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage)
{
2009-02-09 11:35:43 +01:00
ULONG executionStatus;
if (!getExecutionStatus(m_cif.debugControl, &executionStatus, errorMessage))
return false;
if (debugCDB)
qDebug() << Q_FUNC_INFO << "\n ex=" << executionStatus;
if (executionStatus == DEBUG_STATUS_GO) {
m_engine->warning(QLatin1String("continueInferior() called while debuggee is running."));
return true;
}
clearForRun();
m_engine->killWatchTimer();
m_debuggerManager->resetLocation();
m_debuggerManagerAccess->notifyInferiorRunningRequested();
if (!continueInferiorProcess(errorMessage))
return false;
m_engine->startWatchTimer();
m_debuggerManagerAccess->notifyInferiorRunning();
return true;
2009-02-09 11:35:43 +01:00
}
bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage)
2009-02-09 11:35:43 +01:00
{
// Interrupt the interferior process without notifications
if (debugCDB) {
ULONG executionStatus;
getExecutionStatus(m_cif.debugControl, &executionStatus, errorMessage);
qDebug() << Q_FUNC_INFO << "\n ex=" << executionStatus;
}
if (!DebugBreakProcess(m_hDebuggeeProcess)) {
*errorMessage = QString::fromLatin1("DebugBreakProcess failed: %1").arg(Core::Utils::winErrorMessage(GetLastError()));
return false;
}
#if 0
const HRESULT hr = m_cif.debugControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE|DEBUG_INTERRUPT_EXIT);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to interrupt debuggee after %1s: %2").
arg(getInterruptTimeOutSecs(m_cif.debugControl)).arg(msgComFailed("SetInterrupt", hr));
return false;
}
#endif
return true;
}
2009-02-23 14:46:46 +01:00
void CdbDebugEngine::interruptInferior()
{
if (!m_d->m_hDebuggeeProcess || !m_d->isDebuggeeRunning())
2009-02-09 11:35:43 +01:00
return;
QString errorMessage;
if (m_d->interruptInterferiorProcess(&errorMessage)) {
m_d->m_debuggerManagerAccess->notifyInferiorStopped();
} else {
warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
2009-02-09 11:35:43 +01:00
}
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::runToLineExec(const QString &fileName, int lineNumber)
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO << fileName << lineNumber;
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::runToFunctionExec(const QString &functionName)
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO << functionName;
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::jumpToLineExec(const QString &fileName, int lineNumber)
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO << fileName << lineNumber;
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &value)
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO << expr << value;
const int frameIndex = m_d->m_debuggerManagerAccess->stackHandler()->currentIndex();
QString errorMessage;
bool success = false;
do {
QString newValue;
CdbStackFrameContext *sg = m_d->getStackFrameContext(frameIndex, &errorMessage);
if (!sg)
break;
if (!sg->assignValue(expr, value, &newValue, &errorMessage))
break;
// Update view
WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
2009-06-17 17:34:48 +02:00
if (WatchData *fwd = watchHandler->findItem(expr)) {
fwd->setValue(newValue);
watchHandler->insertData(*fwd);
2009-06-17 17:34:48 +02:00
watchHandler->updateWatchers();
}
success = true;
} while (false);
if (!success) {
const QString msg = tr("Unable to assign the value '%1' to '%2': %3").arg(value, expr, errorMessage);
warning(msg);
}
2009-02-09 11:35:43 +01:00
}
2009-02-23 14:46:46 +01:00
void CdbDebugEngine::executeDebuggerCommand(const QString &command)
{
QString errorMessage;
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_d->m_cif.debugControl, command, &errorMessage))
warning(errorMessage);
}
bool CdbDebugEnginePrivate::executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage)
2009-02-09 11:35:43 +01:00
{
// output to all clients, else we do not see anything
const HRESULT hr = ctrl->ExecuteWide(DEBUG_OUTCTL_ALL_CLIENTS, reinterpret_cast<PCWSTR>(command.utf16()), 0);
if (debugCDB)
qDebug() << "executeDebuggerCommand" << command << SUCCEEDED(hr);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to execute '%1': %2").
arg(command, msgDebugEngineComResult(hr));
return false;
}
return true;
}
bool CdbDebugEngine::evaluateExpression(const QString &expression,
QString *value,
QString *type,
QString *errorMessage)
{
DEBUG_VALUE debugValue;
if (!m_d->evaluateExpression(m_d->m_cif.debugControl, expression, &debugValue, errorMessage))
return false;
*value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type);
return true;
}
bool CdbDebugEnginePrivate::evaluateExpression(CIDebugControl *ctrl,
const QString &expression,
DEBUG_VALUE *debugValue,
QString *errorMessage)
{
if (debugCDB > 1)
qDebug() << Q_FUNC_INFO << expression;
memset(debugValue, 0, sizeof(DEBUG_VALUE));
// Original syntax must be restored, else setting breakpoints will fail.
SyntaxSetter syntaxSetter(ctrl, DEBUG_EXPR_CPLUSPLUS);
ULONG errorPosition = 0;
const HRESULT hr = ctrl->EvaluateWide(reinterpret_cast<PCWSTR>(expression.utf16()),
DEBUG_VALUE_INVALID, debugValue,
&errorPosition);
if (FAILED(hr)) {
if (HRESULT_CODE(hr) == 517) {
*errorMessage = QString::fromLatin1("Unable to evaluate '%1': Expression out of scope.").
arg(expression);
} else {
*errorMessage = QString::fromLatin1("Unable to evaluate '%1': Error at %2: %3").
arg(expression).arg(errorPosition).arg(msgDebugEngineComResult(hr));
}
return false;
}
return true;
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::activateFrame(int frameIndex)
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO << frameIndex;
if (m_d->m_debuggerManager->status() != DebuggerInferiorStopped) {
qWarning("WARNING %s: invoked while debuggee is running\n", Q_FUNC_INFO);
2009-02-09 11:35:43 +01:00
return;
}
2009-02-09 11:35:43 +01:00
QString errorMessage;
bool success = false;
do {
StackHandler *stackHandler = m_d->m_debuggerManagerAccess->stackHandler();
2009-03-27 17:19:39 +01:00
WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
const int oldIndex = stackHandler->currentIndex();
if (frameIndex >= stackHandler->stackSize()) {
errorMessage = msgStackIndexOutOfRange(frameIndex, stackHandler->stackSize());
break;
}
2009-02-09 11:35:43 +01:00
2009-03-27 17:19:39 +01:00
if (oldIndex != frameIndex)
stackHandler->setCurrentIndex(frameIndex);
2009-02-09 11:35:43 +01:00
const StackFrame &frame = stackHandler->currentFrame();
if (!frame.isUsable()) {
// Clean out model
2009-06-17 17:34:48 +02:00
watchHandler->beginCycle();
watchHandler->endCycle();
errorMessage = QString::fromLatin1("%1: file %2 unusable.").
arg(QLatin1String(Q_FUNC_INFO), frame.file);
break;
}
2009-02-23 14:46:46 +01:00
m_d->m_debuggerManager->gotoLocation(frame.file, frame.line, true);
if (oldIndex != frameIndex || m_d->m_firstActivatedFrame) {
watchHandler->beginCycle();
if (CdbStackFrameContext *sgc = m_d->getStackFrameContext(frameIndex, &errorMessage))
success = sgc->populateModelInitially(watchHandler, &errorMessage);
watchHandler->endCycle();
}
} while (false);
if (!success)
warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
2009-03-27 17:19:39 +01:00
m_d->m_firstActivatedFrame = false;
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::selectThread(int index)
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO << index;
2009-02-09 11:35:43 +01:00
//reset location arrow
2009-02-23 14:46:46 +01:00
m_d->m_debuggerManager->resetLocation();
2009-02-09 11:35:43 +01:00
2009-02-23 14:46:46 +01:00
ThreadsHandler *threadsHandler = m_d->m_debuggerManagerAccess->threadsHandler();
2009-02-09 11:35:43 +01:00
threadsHandler->setCurrentThread(index);
2009-02-20 17:07:00 +01:00
m_d->m_currentThreadId = index;
m_d->updateStackTrace();
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::attemptBreakpointSynchronization()
2009-02-09 11:35:43 +01:00
{
QString errorMessage;
if (!m_d->attemptBreakpointSynchronization(&errorMessage))
warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
}
2009-02-23 14:46:46 +01:00
bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessage)
{
if (!m_hDebuggeeProcess) {
*errorMessage = QLatin1String("attemptBreakpointSynchronization() called while debugger is not running");
return false;
}
// This is called from
// 1) CreateProcessEvent with the halted engine
// 2) from the break handler, potentially while the debuggee is running
// If the debuggee is running (for which the execution status is
// no reliable indicator), we temporarily halt and have ourselves
// called again from the debug event handler.
ULONG dummy;
const bool wasRunning = !CDBBreakPoint::getBreakPointCount(m_cif.debugControl, &dummy);
if (debugCDB)
qDebug() << Q_FUNC_INFO << "\n Running=" << wasRunning;
if (wasRunning) {
const HandleBreakEventMode oldMode = m_breakEventMode;
m_breakEventMode = BreakEventSyncBreakPoints;
if (!interruptInterferiorProcess(errorMessage)) {
m_breakEventMode = oldMode;
return false;
}
return true;
2009-02-23 14:46:46 +01:00
}
return CDBBreakPoint::synchronizeBreakPoints(m_cif.debugControl,
m_cif.debugSymbols,
m_debuggerManagerAccess->breakHandler(),
errorMessage);
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::reloadDisassembler()
{
enum { ContextLines = 40 };
// Do we have a top stack frame?
const ULONG64 offset = m_d->m_currentStackTrace ? m_d->m_currentStackTrace->instructionOffset() : ULONG64(0);
if (debugCDB)
qDebug() << Q_FUNC_INFO << offset;
DisassemblerHandler *dh = m_d->m_debuggerManagerAccess->disassemblerHandler();
if (offset) {
QList<DisassemblerLine> lines;
QString errorMessage;
QApplication::setOverrideCursor(Qt::WaitCursor);
const bool drc = dissassemble(m_d->m_cif.debugClient, m_d->m_cif.debugControl, offset,
ContextLines, ContextLines, &lines, &errorMessage);
QApplication::restoreOverrideCursor();
if (drc) {
dh->setLines(lines);
if (lines.size() > ContextLines)
dh->setCurrentLine(ContextLines);
} else {
warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
}
} else {
dh->setLines(QList<DisassemblerLine>());
}
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::reloadModules()
2009-02-09 11:35:43 +01:00
{
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::loadSymbols(const QString &moduleName)
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO << moduleName;
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::loadAllSymbols()
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO;
2009-02-09 11:35:43 +01:00
}
2009-04-15 12:04:27 +02:00
QList<Symbol> CdbDebugEngine::moduleSymbols(const QString &moduleName)
{
QList<Symbol> rc;
QString errorMessage;
bool success = false;
do {
if (m_d->isDebuggeeRunning()) {
errorMessage = tr("Cannot retrieve symbols while the debuggee is running.");
break;
}
if (!getModuleSymbols(m_d->m_cif.debugSymbols, moduleName, &rc, &errorMessage))
2009-04-15 12:04:27 +02:00
break;
success = true;
} while (false);
if (!success)
warning(errorMessage);
2009-04-15 12:04:27 +02:00
return rc;
}
static inline int registerFormatBase()
{
switch(checkedRegisterFormatAction()) {
case FormatHexadecimal:
return 16;
case FormatDecimal:
return 10;
case FormatOctal:
return 8;
case FormatBinary:
return 2;
break;
case FormatRaw:
case FormatNatural:
break;
}
return 10;
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::reloadRegisters()
2009-02-09 11:35:43 +01:00
{
const int intBase = registerFormatBase();
if (debugCDB)
qDebug() << Q_FUNC_INFO << intBase;
QList<Register> registers;
QString errorMessage;
if (!getRegisters(m_d->m_cif.debugControl, m_d->m_cif.debugRegisters, &registers, &errorMessage, intBase))
warning(msgFunctionFailed("reloadRegisters" , errorMessage));
m_d->m_debuggerManagerAccess->registerHandler()->setRegisters(registers);
2009-02-09 11:35:43 +01:00
}
2009-02-09 13:07:38 +01:00
void CdbDebugEngine::timerEvent(QTimerEvent* te)
{
// Fetch away the debug events and notify if debuggee
// stops. Note that IDebugEventCallback does not
// cover all cases of a debuggee stopping execution
// (such as step over,etc).
2009-02-20 17:07:00 +01:00
if (te->timerId() != m_d->m_watchTimer)
2009-02-09 11:35:43 +01:00
return;
const HRESULT hr = m_d->m_cif.debugControl->WaitForEvent(0, 1);
if (debugCDB)
if (debugCDB > 1 || hr != S_FALSE)
qDebug() << Q_FUNC_INFO << "WaitForEvent" << m_d->m_debuggerManager->status() << msgDebugEngineComResult(hr);
2009-02-23 14:46:46 +01:00
2009-02-09 11:35:43 +01:00
switch (hr) {
case S_OK:
killWatchTimer();
2009-02-20 17:07:00 +01:00
m_d->handleDebugEvent();
2009-02-09 11:35:43 +01:00
break;
case S_FALSE:
case E_PENDING:
case E_FAIL:
2009-02-09 11:35:43 +01:00
break;
case E_UNEXPECTED: // Occurs on ExitProcess.
2009-02-09 11:35:43 +01:00
killWatchTimer();
break;
2009-02-09 11:35:43 +01:00
}
}
void CdbDebugEngine::slotConsoleStubStarted()
{
const qint64 appPid = m_d->m_consoleStubProc.applicationPID();
if (debugCDB)
qDebug() << Q_FUNC_INFO << appPid;
// Attach to console process
QString errorMessage;
if (startAttachDebugger(appPid, AttachExternal, &errorMessage)) {
startWatchTimer();
m_d->m_debuggerManagerAccess->notifyInferiorPidChanged(appPid);
m_d->m_debuggerManagerAccess->notifyInferiorRunning();
} else {
QMessageBox::critical(m_d->m_debuggerManager->mainWindow(), tr("Debugger Error"), errorMessage);
}
}
void CdbDebugEngine::slotConsoleStubError(const QString &msg)
{
QMessageBox::critical(m_d->m_debuggerManager->mainWindow(), tr("Debugger Error"), msg);
}
void CdbDebugEngine::slotConsoleStubTerminated()
{
exitDebugger();
}
void CdbDebugEngine::slotAttachedCrashed()
{
m_d->m_debuggerManagerAccess->showDebuggerOutput("A","A");
m_d->handleDebugEvent();
}
void CdbDebugEngine::warning(const QString &w)
{
static const QString prefix = QLatin1String("warning:");
m_d->m_debuggerManagerAccess->showDebuggerOutput(prefix, w);
qWarning("%s\n", qPrintable(w));
}
void CdbDebugEnginePrivate::notifyCrashed()
{
// Cannot go over crash point to execute calls.
m_dumper->disable();
}
2009-02-20 17:07:00 +01:00
void CdbDebugEnginePrivate::handleDebugEvent()
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << m_hDebuggeeProcess << m_breakEventMode
<< executionStatusString(m_cif.debugControl);
2009-02-23 14:46:46 +01:00
// restore mode and do special handling
const HandleBreakEventMode mode = m_breakEventMode;
m_breakEventMode = BreakEventHandle;
switch (mode) {
case BreakEventHandle:
2009-02-23 14:46:46 +01:00
m_debuggerManagerAccess->notifyInferiorStopped();
2009-02-09 11:35:43 +01:00
updateThreadList();
updateStackTrace();
break;
case BreakEventIgnoreOnce:
m_engine->startWatchTimer();
break;
case BreakEventSyncBreakPoints: {
// Temp stop to sync breakpoints
QString errorMessage;
attemptBreakpointSynchronization(&errorMessage);
m_engine->startWatchTimer();
continueInferiorProcess(&errorMessage);
if (!errorMessage.isEmpty())
m_engine->warning(QString::fromLatin1("In handleDebugEvent: %1").arg(errorMessage));
}
break;
2009-02-09 11:35:43 +01:00
}
}
void CdbDebugEnginePrivate::setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << hDebuggeeProcess << hDebuggeeThread;
m_hDebuggeeProcess = hDebuggeeProcess;
m_hDebuggeeThread = hDebuggeeThread;
}
2009-02-20 17:07:00 +01:00
void CdbDebugEnginePrivate::updateThreadList()
2009-02-09 11:35:43 +01:00
{
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO << m_hDebuggeeProcess;
2009-02-23 14:46:46 +01:00
ThreadsHandler* th = m_debuggerManagerAccess->threadsHandler();
2009-02-09 11:35:43 +01:00
QList<ThreadData> threads;
bool success = false;
QString errorMessage;
do {
ULONG numberOfThreads;
HRESULT hr= m_cif.debugSystemObjects->GetNumberThreads(&numberOfThreads);
if (FAILED(hr)) {
errorMessage= msgComFailed("GetNumberThreads", hr);
break;
}
const ULONG maxThreadIds = 256;
ULONG threadIds[maxThreadIds];
ULONG biggestThreadId = qMin(maxThreadIds, numberOfThreads - 1);
hr = m_cif.debugSystemObjects->GetThreadIdsByIndex(0, biggestThreadId, threadIds, 0);
if (FAILED(hr)) {
errorMessage= msgComFailed("GetThreadIdsByIndex", hr);
break;
}
for (ULONG threadId = 0; threadId <= biggestThreadId; ++threadId) {
ThreadData thread;
thread.id = threadId;
threads.append(thread);
}
2009-02-09 11:35:43 +01:00
th->setThreads(threads);
success = true;
} while (false);
if (!success)
m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
2009-02-09 11:35:43 +01:00
}
void CdbDebugEnginePrivate::updateStackTrace()
{
if (debugCDB)
qDebug() << Q_FUNC_INFO;
// Create a new context
clearForRun();
QString errorMessage;
m_engine->reloadRegisters();
m_currentStackTrace =
CdbStackTraceContext::create(m_dumper, m_currentThreadId, &errorMessage);
if (!m_currentStackTrace) {
m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
return;
}
// Disassembling slows things down a bit. Assembler is still available via menu.
#if 0
m_engine->reloadDisassembler(); // requires stack trace
#endif
const QList<StackFrame> stackFrames = m_currentStackTrace->frames();
// find the first usable frame and select it
int current = -1;
const int count = stackFrames.count();
for (int i=0; i < count; ++i)
if (stackFrames.at(i).isUsable()) {
current = i;
break;
}
2009-02-09 11:35:43 +01:00
m_debuggerManagerAccess->stackHandler()->setFrames(stackFrames);
m_firstActivatedFrame = true;
if (current >= 0) {
m_debuggerManagerAccess->stackHandler()->setCurrentIndex(current);
m_engine->activateFrame(current);
}
m_debuggerManagerAccess->watchHandler()->updateWatchers();
2009-02-09 11:35:43 +01:00
}
void CdbDebugEnginePrivate::updateModules()
2009-02-09 11:35:43 +01:00
{
QList<Module> modules;
QString errorMessage;
if (!getModuleList(m_cif.debugSymbols, &modules, &errorMessage))
m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
m_debuggerManagerAccess->modulesHandler()->setModules(modules);
2009-02-09 11:35:43 +01:00
}
static const char *dumperPrefixC = "dumper";
void CdbDebugEnginePrivate::handleModuleLoad(const QString &name)
{
if (debugCDB>2)
qDebug() << Q_FUNC_INFO << "\n " << name;
m_dumper->moduleLoadHook(name, m_hDebuggeeProcess);
updateModules();
}
void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP)
2009-02-09 11:35:43 +01:00
{
2009-02-20 14:56:36 +01:00
Q_UNUSED(pBP)
2009-02-23 14:46:46 +01:00
if (debugCDB)
qDebug() << Q_FUNC_INFO;
2009-02-09 11:35:43 +01:00
}
2009-02-20 14:56:36 +01:00
void CdbDebugEngine::reloadSourceFiles()
{
}
2009-02-23 16:13:35 +01:00
QStringList CdbDebugEnginePrivate::sourcePaths() const
{
WCHAR wszBuf[MAX_PATH];
if (SUCCEEDED(m_cif.debugSymbols->GetSourcePathWide(wszBuf, MAX_PATH, 0)))
return QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)).split(QLatin1Char(';'));
return QStringList();
}
void CdbDebugEngine::syncDebuggerPaths()
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << m_d->m_options->symbolPaths << m_d->m_options->sourcePaths;
QString errorMessage;
if (!m_d->setSourcePaths(m_d->m_options->sourcePaths, &errorMessage)
|| !m_d->setSymbolPaths(m_d->m_options->symbolPaths, &errorMessage)) {
errorMessage = QString::fromLatin1("Unable to set the debugger paths: %1").arg(errorMessage);
warning(errorMessage);
}
}
static inline QString pathString(const QStringList &s)
{ return s.join(QString(QLatin1Char(';'))); }
bool CdbDebugEnginePrivate::setSourcePaths(const QStringList &s, QString *errorMessage)
{
const HRESULT hr = m_cif.debugSymbols->SetSourcePathWide(reinterpret_cast<PCWSTR>(pathString(s).utf16()));
if (FAILED(hr)) {
if (errorMessage)
*errorMessage = msgComFailed("SetSourcePathWide", hr);
return false;
}
return true;
}
QStringList CdbDebugEnginePrivate::symbolPaths() const
{
WCHAR wszBuf[MAX_PATH];
if (SUCCEEDED(m_cif.debugSymbols->GetSymbolPathWide(wszBuf, MAX_PATH, 0)))
return QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)).split(QLatin1Char(';'));
return QStringList();
}
bool CdbDebugEnginePrivate::setSymbolPaths(const QStringList &s, QString *errorMessage)
{
const HRESULT hr = m_cif.debugSymbols->SetSymbolPathWide(reinterpret_cast<PCWSTR>(pathString(s).utf16()));
if (FAILED(hr)) {
if (errorMessage)
*errorMessage = msgComFailed("SetSymbolPathWide", hr);
return false;
}
return true;
}
2009-02-23 16:13:35 +01:00
} // namespace Internal
} // namespace Debugger
// Accessed by DebuggerManager
Debugger::Internal::IDebuggerEngine *createWinEngine(Debugger::Internal::DebuggerManager *parent,
bool cmdLineEnabled,
QList<Core::IOptionsPage*> *opts)
{
// Create options page
QSharedPointer<Debugger::Internal::CdbOptions> options(new Debugger::Internal::CdbOptions);
options->fromSettings(Core::ICore::instance()->settings());
Debugger::Internal::CdbOptionsPage *optionsPage = new Debugger::Internal::CdbOptionsPage(options);
opts->push_back(optionsPage);
if (!cmdLineEnabled || !options->enabled)
return 0;
// Create engine
QString errorMessage;
Debugger::Internal::IDebuggerEngine *engine =
Debugger::Internal::CdbDebugEngine::create(parent, options, &errorMessage);
if (!engine) {
optionsPage->setFailureMessage(errorMessage);
qWarning("%s\n" ,qPrintable(errorMessage));
}
QObject::connect(optionsPage, SIGNAL(debuggerPathsChanged()), engine, SLOT(syncDebuggerPaths()));
return engine;
2009-02-23 16:13:35 +01:00
}