2009-04-20 16:40:50 +02:00
|
|
|
/**************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** 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"
|
2009-04-22 17:28:26 +02:00
|
|
|
#include "watchutils.h"
|
2009-04-20 16:40:50 +02:00
|
|
|
|
|
|
|
|
#include <QtCore/QRegExp>
|
2009-04-22 17:28:26 +02:00
|
|
|
#include <QtCore/QCoreApplication>
|
|
|
|
|
#include <QtCore/QTextStream>
|
2009-04-20 16:40:50 +02:00
|
|
|
|
|
|
|
|
enum { loadDebug = 0 };
|
|
|
|
|
|
2009-04-22 17:28:26 +02:00
|
|
|
static const char *dumperModuleNameC = "gdbmacros";
|
|
|
|
|
static const char *qtCoreModuleNameC = "QtCore";
|
|
|
|
|
static const ULONG waitTimeOutMS = 30000;
|
|
|
|
|
|
2009-04-20 16:40:50 +02:00
|
|
|
namespace Debugger {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
// Alloc memory in debuggee using the ".dvalloc" command as
|
|
|
|
|
// there seems to be no API for it.
|
2009-04-21 12:30:12 +02:00
|
|
|
static bool allocDebuggeeMemory(CIDebugControl *ctl,
|
|
|
|
|
CIDebugClient *client,
|
2009-04-20 16:40:50 +02:00
|
|
|
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;
|
2009-04-22 17:28:26 +02:00
|
|
|
// "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
|
2009-04-20 16:40:50 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2009-04-22 17:28:26 +02:00
|
|
|
if (loadDebug)
|
|
|
|
|
qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok;
|
2009-04-20 16:40:50 +02:00
|
|
|
if (!ok) {
|
|
|
|
|
*errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Alloc an AscII string in debuggee
|
2009-04-21 12:30:12 +02:00
|
|
|
static bool createDebuggeeAscIIString(CIDebugControl *ctl,
|
|
|
|
|
CIDebugClient *client,
|
|
|
|
|
CIDebugDataSpaces *data,
|
2009-04-20 16:40:50 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
2009-04-21 12:30:12 +02:00
|
|
|
static bool debuggeeLoadLibrary(CIDebugControl *ctl,
|
|
|
|
|
CIDebugClient *client,
|
|
|
|
|
CIDebugSymbols *syms,
|
|
|
|
|
CIDebugDataSpaces *data,
|
2009-04-20 16:40:50 +02:00
|
|
|
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".
|
2009-04-22 17:28:26 +02:00
|
|
|
// Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some
|
|
|
|
|
// reason, the symbol is present in QtGui as well without type information.
|
|
|
|
|
QString dummyFunc = QLatin1String("*qstrdup");
|
|
|
|
|
if (resolveSymbol(syms, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
|
2009-04-20 16:40:50 +02:00
|
|
|
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;
|
2009-04-22 17:28:26 +02:00
|
|
|
// This will hit a breakpoint.
|
2009-04-20 16:40:50 +02:00
|
|
|
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QString(QLatin1Char('g')), errorMessage))
|
2009-04-22 17:28:26 +02:00
|
|
|
return false;
|
|
|
|
|
const HRESULT hr = ctl->WaitForEvent(0, waitTimeOutMS);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgComFailed("WaitForEvent", hr);
|
2009-04-20 16:40:50 +02:00
|
|
|
return false;
|
2009-04-22 17:28:26 +02:00
|
|
|
}
|
2009-04-20 16:40:50 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-22 17:28:26 +02:00
|
|
|
// ------------------- CdbDumperHelper::DumperInputParameters
|
|
|
|
|
struct CdbDumperHelper::DumperInputParameters {
|
|
|
|
|
DumperInputParameters(int protocolVersion_ = 1,
|
|
|
|
|
int token_ = 1,
|
|
|
|
|
quint64 Address_ = 0,
|
|
|
|
|
bool dumpChildren_ = false,
|
|
|
|
|
int extraInt0_ = 0,
|
|
|
|
|
int extraInt1_ = 0,
|
|
|
|
|
int extraInt2_ = 0,
|
|
|
|
|
int extraInt3_ = 0);
|
|
|
|
|
|
|
|
|
|
QString command(const QString &dumpFunction) const;
|
|
|
|
|
|
|
|
|
|
int protocolVersion;
|
|
|
|
|
int token;
|
|
|
|
|
quint64 address;
|
|
|
|
|
bool dumpChildren;
|
|
|
|
|
int extraInt0;
|
|
|
|
|
int extraInt1;
|
|
|
|
|
int extraInt2;
|
|
|
|
|
int extraInt3;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CdbDumperHelper::DumperInputParameters::DumperInputParameters(int protocolVersion_,
|
|
|
|
|
int token_,
|
|
|
|
|
quint64 address_,
|
|
|
|
|
bool dumpChildren_,
|
|
|
|
|
int extraInt0_,
|
|
|
|
|
int extraInt1_,
|
|
|
|
|
int extraInt2_,
|
|
|
|
|
int extraInt3_) :
|
|
|
|
|
protocolVersion(protocolVersion_),
|
|
|
|
|
token(token_),
|
|
|
|
|
address(address_),
|
|
|
|
|
dumpChildren(dumpChildren_),
|
|
|
|
|
extraInt0(extraInt0_),
|
|
|
|
|
extraInt1(extraInt1_),
|
|
|
|
|
extraInt2(extraInt2_),
|
|
|
|
|
extraInt3(extraInt3_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString CdbDumperHelper::DumperInputParameters::command(const QString &dumpFunction) const
|
|
|
|
|
{
|
|
|
|
|
QString rc;
|
|
|
|
|
QTextStream str(&rc);
|
|
|
|
|
str.setIntegerBase(16);
|
|
|
|
|
str << ".call " << dumpFunction << '(' << protocolVersion << ',' << token << ',';
|
|
|
|
|
str.setIntegerBase(16);
|
|
|
|
|
str << "0x" << address;
|
|
|
|
|
str.setIntegerBase(10);
|
|
|
|
|
str << ',' << (dumpChildren ? 1 : 0) << ',' << extraInt0 << ',' << extraInt1
|
|
|
|
|
<< ',' << extraInt2 << ',' << extraInt3 << ')';
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------- CdbDumperHelper
|
|
|
|
|
|
|
|
|
|
CdbDumperHelper::CdbDumperHelper(CdbComInterfaces *cif) :
|
2009-04-20 16:40:50 +02:00
|
|
|
m_state(NotLoaded),
|
2009-04-22 17:28:26 +02:00
|
|
|
m_cif(cif),
|
|
|
|
|
m_inBufferAddress(0),
|
|
|
|
|
m_inBufferSize(0),
|
|
|
|
|
m_outBufferAddress(0),
|
|
|
|
|
m_outBufferSize(0),
|
|
|
|
|
m_buffer(0)
|
2009-04-20 16:40:50 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-22 17:28:26 +02:00
|
|
|
CdbDumperHelper::~CdbDumperHelper()
|
|
|
|
|
{
|
|
|
|
|
clearBuffer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CdbDumperHelper::clearBuffer()
|
|
|
|
|
{
|
|
|
|
|
if (m_buffer) {
|
|
|
|
|
delete [] m_buffer;
|
|
|
|
|
m_buffer = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-20 16:40:50 +02:00
|
|
|
void CdbDumperHelper::reset(const QString &library, bool enabled)
|
|
|
|
|
{
|
|
|
|
|
m_library = library;
|
|
|
|
|
m_state = enabled ? NotLoaded : Disabled;
|
|
|
|
|
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
|
2009-04-22 17:28:26 +02:00
|
|
|
m_knownTypes.clear();
|
|
|
|
|
m_qtVersion.clear();
|
|
|
|
|
m_qtNamespace.clear();
|
|
|
|
|
m_inBufferAddress = m_outBufferAddress = 0;
|
|
|
|
|
m_inBufferSize = m_outBufferSize = 0;
|
|
|
|
|
clearBuffer();
|
2009-04-20 16:40:50 +02:00
|
|
|
}
|
|
|
|
|
|
2009-04-22 17:28:26 +02:00
|
|
|
// Attempt to load and initialize dumpers, give feedback
|
|
|
|
|
// to user.
|
|
|
|
|
void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEngines *access)
|
2009-04-20 16:40:50 +02:00
|
|
|
{
|
2009-04-22 17:28:26 +02:00
|
|
|
enum Result { Failed, Succeeded, NoQtApp };
|
|
|
|
|
|
|
|
|
|
if (m_state != NotLoaded)
|
|
|
|
|
return;
|
|
|
|
|
manager->showStatusMessage(QCoreApplication::translate("CdbDumperHelper", "Loading dumpers..."), 10000);
|
|
|
|
|
const QString messagePrefix = QLatin1String("dumper:");
|
|
|
|
|
QString message;
|
|
|
|
|
Result result = Failed;
|
|
|
|
|
do {
|
|
|
|
|
// Do we have Qt and are we already loaded by accident?
|
|
|
|
|
QStringList modules;
|
|
|
|
|
if (!getModuleNameList(m_cif->debugSymbols, &modules, &message))
|
|
|
|
|
break;
|
|
|
|
|
if (modules.filter(QLatin1String(qtCoreModuleNameC)).isEmpty()) {
|
|
|
|
|
message = QCoreApplication::translate("CdbDumperHelper", "The debugger does not appear to be Qt application.");
|
|
|
|
|
result = NoQtApp;
|
2009-04-20 16:40:50 +02:00
|
|
|
}
|
2009-04-22 17:28:26 +02:00
|
|
|
// Make sure the dumper lib is loaded.
|
|
|
|
|
if (modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty()) {
|
|
|
|
|
// Try to load
|
|
|
|
|
if (!debuggeeLoadLibrary(m_cif->debugControl, m_cif->debugClient, m_cif->debugSymbols, m_cif->debugDataSpaces,
|
|
|
|
|
m_library, &message)) {
|
|
|
|
|
break;
|
2009-04-20 16:40:50 +02:00
|
|
|
}
|
2009-04-22 17:28:26 +02:00
|
|
|
} else {
|
|
|
|
|
access->showDebuggerOutput(messagePrefix, QCoreApplication::translate("CdbDumperHelper", "The dumper module appears to be already loaded."));
|
2009-04-20 16:40:50 +02:00
|
|
|
}
|
2009-04-22 17:28:26 +02:00
|
|
|
// Resolve symbols and do call to get types
|
|
|
|
|
if (!resolveSymbols(&message))
|
|
|
|
|
break;
|
|
|
|
|
if (!getKnownTypes(&message))
|
|
|
|
|
break;
|
|
|
|
|
message = QCoreApplication::translate("CdbDumperHelper", "Dumper library '%1' loaded.").arg(m_library);
|
|
|
|
|
result = Succeeded;
|
|
|
|
|
} while (false);
|
|
|
|
|
// eval state and notify user
|
|
|
|
|
switch (result) {
|
|
|
|
|
case Failed:
|
|
|
|
|
message = QCoreApplication::translate("CdbDumperHelper", "The dumper library '%1' could not be loaded:\n%2").arg(m_library, message);
|
|
|
|
|
access->showDebuggerOutput(messagePrefix, message);
|
|
|
|
|
access->showQtDumperLibraryWarning(message);
|
|
|
|
|
m_state = Disabled;
|
2009-04-20 16:40:50 +02:00
|
|
|
break;
|
2009-04-22 17:28:26 +02:00
|
|
|
case Succeeded:
|
|
|
|
|
access->showDebuggerOutput(messagePrefix, message);
|
|
|
|
|
access->showDebuggerOutput(messagePrefix, statusMessage());
|
|
|
|
|
m_state = Loaded;
|
2009-04-20 16:40:50 +02:00
|
|
|
break;
|
2009-04-22 17:28:26 +02:00
|
|
|
case NoQtApp:
|
|
|
|
|
access->showDebuggerOutput(messagePrefix, message);
|
|
|
|
|
m_state = Disabled;
|
2009-04-20 16:40:50 +02:00
|
|
|
break;
|
2009-04-22 17:28:26 +02:00
|
|
|
}
|
|
|
|
|
if (loadDebug)
|
|
|
|
|
qDebug() << Q_FUNC_INFO << "\n<" << result << '>' << m_state << message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString CdbDumperHelper::statusMessage() const
|
|
|
|
|
{
|
|
|
|
|
const QString nameSpace = m_qtNamespace.isEmpty() ? QCoreApplication::translate("CdbDumperHelper", "<none>") : m_qtNamespace;
|
|
|
|
|
return QCoreApplication::translate("CdbDumperHelper",
|
|
|
|
|
"%n known types, Qt version: %1, Qt namespace: %2",
|
|
|
|
|
0, QCoreApplication::CodecForTr,
|
|
|
|
|
m_knownTypes.size()).arg(m_qtVersion, nameSpace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Retrieve address and optionally size of a symbol.
|
|
|
|
|
static inline bool getSymbolAddress(CIDebugSymbols *sg,
|
|
|
|
|
const QString &name,
|
|
|
|
|
quint64 *address,
|
|
|
|
|
ULONG *size /* = 0*/,
|
|
|
|
|
QString *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
// Get address
|
|
|
|
|
HRESULT hr = sg->GetOffsetByNameWide(name.utf16(), address);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgComFailed("GetOffsetByNameWide", hr);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Get size. Even works for arrays
|
|
|
|
|
if (size) {
|
|
|
|
|
ULONG64 moduleAddress;
|
|
|
|
|
ULONG type;
|
|
|
|
|
hr = sg->GetOffsetTypeId(*address, &type, &moduleAddress);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgComFailed("GetOffsetTypeId", hr);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
hr = sg->GetTypeSize(moduleAddress, type, size);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgComFailed("GetTypeSize", hr);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} // size desired
|
|
|
|
|
return true;
|
2009-04-20 16:40:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CdbDumperHelper::resolveSymbols(QString *errorMessage)
|
|
|
|
|
{
|
2009-04-22 17:28:26 +02:00
|
|
|
// Resolve the symbols we need (potentially namespaced).
|
|
|
|
|
// There is a 'qDumpInBuffer' in QtCore as well.
|
|
|
|
|
m_dumpObjectSymbol = QLatin1String("*qDumpObjectData440");
|
|
|
|
|
QString inBufferSymbol = QLatin1String("*qDumpInBuffer");
|
|
|
|
|
QString outBufferSymbol = QLatin1String("*qDumpOutBuffer");
|
|
|
|
|
const QString dumperModuleName = QLatin1String(dumperModuleNameC);
|
|
|
|
|
bool rc = resolveSymbol(m_cif->debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk
|
|
|
|
|
&& resolveSymbol(m_cif->debugSymbols, dumperModuleName, &inBufferSymbol, errorMessage) == ResolveSymbolOk
|
|
|
|
|
&& resolveSymbol(m_cif->debugSymbols, dumperModuleName, &outBufferSymbol, errorMessage) == ResolveSymbolOk;
|
|
|
|
|
if (!rc)
|
|
|
|
|
return false;
|
|
|
|
|
// Determine buffer addresses, sizes and alloc buffer
|
|
|
|
|
rc = getSymbolAddress(m_cif->debugSymbols, inBufferSymbol, &m_inBufferAddress, &m_inBufferSize, errorMessage)
|
|
|
|
|
&& getSymbolAddress(m_cif->debugSymbols, outBufferSymbol, &m_outBufferAddress, &m_outBufferSize, errorMessage);
|
|
|
|
|
if (!rc)
|
|
|
|
|
return false;
|
|
|
|
|
m_buffer = new char[qMax(m_inBufferSize, m_outBufferSize)];
|
2009-04-20 16:40:50 +02:00
|
|
|
if (loadDebug)
|
2009-04-22 17:28:26 +02:00
|
|
|
qDebug().nospace() << Q_FUNC_INFO << '\n' << rc << m_dumpObjectSymbol
|
|
|
|
|
<< " buffers at 0x" << QString::number(m_inBufferAddress, 16) << ','
|
|
|
|
|
<< m_inBufferSize << "; 0x"
|
|
|
|
|
<< QString::number(m_outBufferAddress, 16) << ',' << m_outBufferSize << '\n';
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CdbDumperHelper::getKnownTypes(QString *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
QByteArray output;
|
|
|
|
|
if (!callDumper(DumperInputParameters(1), &output, errorMessage)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!parseQueryDumperOutput(output, &m_knownTypes, &m_qtVersion, &m_qtNamespace)) {
|
|
|
|
|
*errorMessage = QString::fromLatin1("Unable to parse the dumper output: '%1'").arg(QString::fromAscii(output));
|
|
|
|
|
}
|
|
|
|
|
if (loadDebug)
|
|
|
|
|
qDebug() << Q_FUNC_INFO << m_knownTypes << m_qtVersion << m_qtNamespace;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CdbDumperHelper::callDumper(const DumperInputParameters &p, QByteArray *output, QString *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
IgnoreDebugEventCallback devNull;
|
|
|
|
|
EventCallbackRedirector eventRedir(m_cif->debugClient, &devNull);
|
|
|
|
|
const QString callCmd = p.command(m_dumpObjectSymbol);
|
|
|
|
|
// Set up call and a temporary breakpoint after it.
|
|
|
|
|
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, callCmd, errorMessage))
|
|
|
|
|
return false;
|
|
|
|
|
// Go and filter away break event
|
|
|
|
|
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, QString(QLatin1Char('g')), errorMessage))
|
|
|
|
|
return false;
|
|
|
|
|
HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgComFailed("WaitForEvent", hr);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Read output
|
|
|
|
|
hr = m_cif->debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
*errorMessage = msgComFailed("ReadVirtual", hr);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// see QDumper implementation
|
|
|
|
|
const char result = m_buffer[0];
|
|
|
|
|
switch (result) {
|
|
|
|
|
case 't':
|
|
|
|
|
break;
|
|
|
|
|
case '+':
|
|
|
|
|
*errorMessage = QString::fromLatin1("Dumper call '%1' resulted in output overflow.").arg(callCmd);
|
|
|
|
|
return false;
|
|
|
|
|
case 'f':
|
|
|
|
|
*errorMessage = QString::fromLatin1("Dumper call '%1' failed.").arg(callCmd);
|
|
|
|
|
return false;
|
|
|
|
|
default:
|
|
|
|
|
*errorMessage = QString::fromLatin1("Dumper call '%1' failed ('%2').").arg(callCmd).arg(QLatin1Char(result));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*output = QByteArray(m_buffer + 1);
|
|
|
|
|
return true;
|
2009-04-20 16:40:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Debugger
|