forked from qt-creator/qt-creator
Start on dumper support for CDB, bug fixes.
Make command execution output shown, use Wide version of Debug Callbacks and output, provide bases classes for other output and event handlers, move common dumper functionality into debugger manager.
This commit is contained in:
223
src/plugins/debugger/cdb/cdbdumperhelper.cpp
Normal file
223
src/plugins/debugger/cdb/cdbdumperhelper.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Qt Software Information (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 qt-sales@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbdumperhelper.h"
|
||||
#include "cdbmodules.h"
|
||||
#include "cdbdebugengine_p.h"
|
||||
#include "cdbdebugoutput.h"
|
||||
#include "cdbdebugeventcallback.h"
|
||||
|
||||
#include <QtCore/QRegExp>
|
||||
|
||||
enum { loadDebug = 0 };
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
// Alloc memory in debuggee using the ".dvalloc" command as
|
||||
// there seems to be no API for it.
|
||||
static bool allocDebuggeeMemory(IDebugControl4 *ctl,
|
||||
IDebugClient5 *client,
|
||||
int size, ULONG64 *address, QString *errorMessage)
|
||||
{
|
||||
*address = 0;
|
||||
const QString allocCmd = QLatin1String(".dvalloc ") + QString::number(size);
|
||||
StringOutputHandler stringHandler;
|
||||
OutputRedirector redir(client, &stringHandler);
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, allocCmd, errorMessage))
|
||||
return false;
|
||||
// "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
|
||||
bool ok = false;
|
||||
const QString output = stringHandler.result();
|
||||
const int lastBlank = output.lastIndexOf(QLatin1Char(' '));
|
||||
if (lastBlank != -1) {
|
||||
const qint64 addri = output.mid(lastBlank + 1).toLongLong(&ok, 16);
|
||||
if (ok)
|
||||
*address = addri;
|
||||
}
|
||||
if (!ok) {
|
||||
*errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Alloc an AscII string in debuggee
|
||||
static bool createDebuggeeAscIIString(IDebugControl4 *ctl,
|
||||
IDebugClient5 *client,
|
||||
IDebugDataSpaces4 *data,
|
||||
const QString &s,
|
||||
ULONG64 *address,
|
||||
QString *errorMessage)
|
||||
{
|
||||
QByteArray sAsciiData = s.toLocal8Bit();
|
||||
sAsciiData += '\0';
|
||||
if (!allocDebuggeeMemory(ctl, client, sAsciiData.size(), address, errorMessage))
|
||||
return false;
|
||||
const HRESULT hr = data->WriteVirtual(*address, sAsciiData.data(), sAsciiData.size(), 0);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgComFailed("WriteVirtual", hr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Locate 'qstrdup' in the (potentially namespaced) corelib. For some
|
||||
// reason, the symbol is present in QtGui as well without type information.
|
||||
static inline QString resolveStrdup(IDebugSymbols3 *syms, QString *errorMessage)
|
||||
{
|
||||
QStringList matches;
|
||||
const QString pattern = QLatin1String("*qstrdup");
|
||||
const QRegExp corelibPattern(QLatin1String("QtCore[d]*4!"));
|
||||
Q_ASSERT(corelibPattern.isValid());
|
||||
if (!searchSymbols(syms, pattern, &matches, errorMessage))
|
||||
return QString();
|
||||
QStringList corelibStrdup = matches.filter(corelibPattern);
|
||||
if (corelibStrdup.isEmpty()) {
|
||||
*errorMessage = QString::fromLatin1("Unable to locate '%1' in '%2' (%3)").
|
||||
arg(pattern, corelibPattern.pattern(), matches.join(QString(QLatin1Char(','))));
|
||||
return QString();
|
||||
}
|
||||
return corelibStrdup.front();
|
||||
}
|
||||
|
||||
// Load a library into the debuggee. Currently requires
|
||||
// the QtCored4.pdb file to be present as we need "qstrdup"
|
||||
// as dummy symbol. This is ok ATM since dumpers only
|
||||
// make sense for Qt apps.
|
||||
static bool debuggeeLoadLibrary(IDebugControl4 *ctl,
|
||||
IDebugClient5 *client,
|
||||
IDebugSymbols3 *syms,
|
||||
IDebugDataSpaces4 *data,
|
||||
const QString &moduleName, QString *errorMessage)
|
||||
{
|
||||
if (loadDebug)
|
||||
qDebug() << Q_FUNC_INFO << moduleName;
|
||||
// Try to ignore the breakpoints
|
||||
IgnoreDebugEventCallback devNull;
|
||||
EventCallbackRedirector eventRedir(client, &devNull);
|
||||
// Make a call to LoadLibraryA. First, reserve memory in debugger
|
||||
// and copy name over.
|
||||
ULONG64 nameAddress;
|
||||
if (!createDebuggeeAscIIString(ctl, client, data, moduleName, &nameAddress, errorMessage))
|
||||
return false;
|
||||
// We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)"
|
||||
// (void* LoadLibraryA(char*)). However, despite providing a symbol
|
||||
// server, the debugger refuses to recognize it as a function.
|
||||
// Set up the call stack with a function of same signature (qstrdup)
|
||||
// and change the call register to LoadLibraryA() before executing "g".
|
||||
// Prepare call.
|
||||
const QString dummyFunc = resolveStrdup(syms, errorMessage);
|
||||
if (dummyFunc.isEmpty())
|
||||
return false;
|
||||
QString callCmd = QLatin1String(".call ");
|
||||
callCmd += dummyFunc;
|
||||
callCmd += QLatin1String("(0x");
|
||||
callCmd += QString::number(nameAddress, 16);
|
||||
callCmd += QLatin1Char(')');
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, callCmd, errorMessage))
|
||||
return false;
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QLatin1String("r eip=Kernel32!LoadLibraryA"), errorMessage))
|
||||
return false;
|
||||
// This will hit a breakpoint
|
||||
if (loadDebug)
|
||||
qDebug() << " executing 'g'";
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QString(QLatin1Char('g')), errorMessage))
|
||||
return false;
|
||||
// @Todo: We cannot evaluate output here as it is asynchronous
|
||||
return true;
|
||||
}
|
||||
|
||||
CdbDumperHelper::CdbDumperHelper(CdbComInterfaces *cif) :
|
||||
m_state(NotLoaded),
|
||||
m_cif(cif)
|
||||
{
|
||||
}
|
||||
|
||||
void CdbDumperHelper::reset(const QString &library, bool enabled)
|
||||
{
|
||||
m_library = library;
|
||||
m_state = enabled ? NotLoaded : Disabled;
|
||||
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
|
||||
m_errorMessage.clear();
|
||||
}
|
||||
|
||||
bool CdbDumperHelper::moduleLoadHook(const QString &name, bool *ignoreNextBreakPoint)
|
||||
{
|
||||
*ignoreNextBreakPoint = false;
|
||||
bool ok = true; // report failure only once
|
||||
switch (m_state) {
|
||||
case Disabled:
|
||||
break;
|
||||
case NotLoaded:
|
||||
// Load once QtCore is there.
|
||||
if (name.contains(QLatin1String("QtCore"))) {
|
||||
if (loadDebug)
|
||||
qDebug() << Q_FUNC_INFO << '\n' << name << m_state;
|
||||
ok = debuggeeLoadLibrary(m_cif->debugControl, m_cif->debugClient, m_cif->debugSymbols, m_cif->debugDataSpaces,
|
||||
m_library, &m_errorMessage);
|
||||
if (ok) {
|
||||
m_state = Loading;
|
||||
*ignoreNextBreakPoint = true;
|
||||
} else {
|
||||
m_state = Failed;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Loading:
|
||||
// Hurray, loaded. Now resolve the symbols we need
|
||||
if (name.contains(QLatin1String("gdbmacros"))) {
|
||||
ok = resolveSymbols(&m_errorMessage);
|
||||
if (ok) {
|
||||
m_state = Loaded;
|
||||
} else {
|
||||
m_state = Failed;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Loaded:
|
||||
break;
|
||||
case Failed:
|
||||
break;
|
||||
};
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool CdbDumperHelper::resolveSymbols(QString *errorMessage)
|
||||
{
|
||||
// Resolve the symbols we need
|
||||
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
|
||||
const bool rc = resolveSymbol(m_cif->debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk;
|
||||
if (loadDebug)
|
||||
qDebug() << Q_FUNC_INFO << '\n' << rc << m_dumpObjectSymbol;
|
||||
return rc;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
Reference in New Issue
Block a user