forked from qt-creator/qt-creator
Debugger: R.I.P. old CDB engine.
This commit is contained in:
@@ -1,97 +0,0 @@
|
||||
include(cdbcore.pri)
|
||||
|
||||
!isEmpty(CDB_PATH) {
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/cdbengine.h \
|
||||
$$PWD/cdbengine_p.h \
|
||||
$$PWD/cdbdebugeventcallback.h \
|
||||
$$PWD/cdbdebugoutput.h \
|
||||
$$PWD/cdbsymbolgroupcontext.h \
|
||||
$$PWD/cdbsymbolgroupcontext_tpl.h \
|
||||
$$PWD/cdbstacktracecontext.h \
|
||||
$$PWD/cdbbreakpoint.h \
|
||||
$$PWD/cdbmodules.h \
|
||||
$$PWD/cdbassembler.h \
|
||||
$$PWD/cdboptions.h \
|
||||
$$PWD/cdboptionspage.h \
|
||||
$$PWD/cdbdumperhelper.h \
|
||||
$$PWD/cdbexceptionutils.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/cdbengine.cpp \
|
||||
$$PWD/cdbdebugeventcallback.cpp \
|
||||
$$PWD/cdbdebugoutput.cpp \
|
||||
$$PWD/cdbsymbolgroupcontext.cpp \
|
||||
$$PWD/cdbstacktracecontext.cpp \
|
||||
$$PWD/cdbbreakpoint.cpp \
|
||||
$$PWD/cdbmodules.cpp \
|
||||
$$PWD/cdbassembler.cpp \
|
||||
$$PWD/cdboptions.cpp \
|
||||
$$PWD/cdboptionspage.cpp \
|
||||
$$PWD/cdbdumperhelper.cpp \
|
||||
$$PWD/cdbexceptionutils.cpp
|
||||
|
||||
FORMS += $$PWD/cdboptionspagewidget.ui
|
||||
}
|
||||
|
||||
|
||||
# Compile test on non-Windows platforms
|
||||
isEmpty(CDB_PATH) {
|
||||
false {
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/cdbcom.h \
|
||||
$$PWD/coreengine.h \
|
||||
$$PWD/debugoutputbase.h \
|
||||
$$PWD/debugeventcallbackbase.h \
|
||||
$$PWD/symbolgroupcontext.h \
|
||||
$$PWD/stacktracecontext.h \
|
||||
$$PWD/corebreakpoint.h
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/cdbengine.h \
|
||||
$$PWD/cdbengine_p.h \
|
||||
$$PWD/cdbdebugeventcallback.h \
|
||||
$$PWD/cdbdebugoutput.h \
|
||||
$$PWD/cdbsymbolgroupcontext.h \
|
||||
$$PWD/cdbsymbolgroupcontext_tpl.h \
|
||||
$$PWD/cdbstacktracecontext.h \
|
||||
$$PWD/cdbbreakpoint.h \
|
||||
$$PWD/cdbmodules.h \
|
||||
$$PWD/cdbassembler.h \
|
||||
$$PWD/cdboptions.h \
|
||||
$$PWD/cdboptionspage.h \
|
||||
$$PWD/cdbdumperhelper.h \
|
||||
$$PWD/cdbsymbolpathlisteditor.h \
|
||||
$$PWD/cdbexceptionutils.h
|
||||
|
||||
SOURCES += \
|
||||
# $$PWD/coreengine.cpp \
|
||||
# $$PWD/debugoutputbase.cpp \
|
||||
# $$PWD/debugeventcallbackbase.cpp \
|
||||
# $$PWD/symbolgroupcontext.cpp \
|
||||
# $$PWD/stacktracecontext.cpp \
|
||||
# $$PWD/corebreakpoint.cpp
|
||||
|
||||
SOURCES += \
|
||||
# $$PWD/cdbengine.cpp \
|
||||
# $$PWD/cdbdebugeventcallback.cpp \
|
||||
$$PWD/cdbdebugoutput.cpp \
|
||||
# $$PWD/cdbsymbolgroupcontext.cpp \
|
||||
$$PWD/cdbstacktracecontext.cpp \
|
||||
$$PWD/cdbbreakpoint.cpp \
|
||||
# $$PWD/cdbmodules.cpp \
|
||||
$$PWD/cdbassembler.cpp \
|
||||
$$PWD/cdboptions.cpp \
|
||||
$$PWD/cdboptionspage.cpp \
|
||||
# $$PWD/cdbdumperhelper.cpp \
|
||||
$$PWD/cdbsymbolpathlisteditor.cpp \
|
||||
# $$PWD/cdbexceptionutils.cpp
|
||||
|
||||
FORMS += $$PWD/cdboptionspagewidget.ui
|
||||
INCLUDEPATH*=$$PWD
|
||||
DEPENDPATH*=$$PWD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbassembler.h"
|
||||
#include "cdbdebugoutput.h"
|
||||
#include "cdbengine_p.h"
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
|
||||
#include "registerhandler.h"
|
||||
|
||||
#include <QtCore/QVector>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
Registers getRegisters(CIDebugControl *ctl,
|
||||
CIDebugRegisters *ireg,
|
||||
QString *errorMessage, int base)
|
||||
{
|
||||
|
||||
ULONG count;
|
||||
HRESULT hr = ireg->GetNumberRegisters(&count);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= CdbCore::msgComFailed("GetNumberRegisters", hr);
|
||||
return Registers();
|
||||
}
|
||||
if (!count)
|
||||
return Registers();
|
||||
Registers registers(count);
|
||||
// Retrieve names
|
||||
char buf[MAX_PATH];
|
||||
for (ULONG r = 0; r < count; r++) {
|
||||
hr = ireg->GetDescription(r, buf, MAX_PATH - 1, 0, 0);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= CdbCore::msgComFailed("GetDescription", hr);
|
||||
return Registers();
|
||||
}
|
||||
registers[r].name = QByteArray(buf);
|
||||
}
|
||||
// get values
|
||||
QVector<DEBUG_VALUE> values(count);
|
||||
DEBUG_VALUE *valuesPtr = &(*values.begin());
|
||||
memset(valuesPtr, 0, count * sizeof(DEBUG_VALUE));
|
||||
hr = ireg->GetValues(count, 0, 0, valuesPtr);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= CdbCore::msgComFailed("GetValues", hr);
|
||||
return Registers();
|
||||
}
|
||||
if (base < 2)
|
||||
base = 10;
|
||||
for (ULONG r = 0; r < count; r++)
|
||||
registers[r].value = CdbCore::debugValueToString(values.at(r), 0, base, ctl);
|
||||
return registers;
|
||||
}
|
||||
|
||||
bool setRegisterValueU64(CIDebugRegisters *ireg, unsigned index, quint64 value, QString *errorMessage)
|
||||
{
|
||||
DEBUG_VALUE debugValueSet;
|
||||
debugValueSet.Type = DEBUG_VALUE_INT64;
|
||||
debugValueSet.I64 = value;
|
||||
const HRESULT hr = ireg->SetValue(index, &debugValueSet);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= CdbCore::msgComFailed("SetValue", hr);
|
||||
false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setRegisterValueU64(CIDebugControl *ctl,
|
||||
CIDebugRegisters *ireg,
|
||||
const QString &name, quint64 value,
|
||||
QString *errorMessage)
|
||||
{
|
||||
// Look up register by name
|
||||
const Registers registers = getRegisters(ctl, ireg, errorMessage);
|
||||
const int regCount = registers.size();
|
||||
for (int r = 0; r < regCount; r++)
|
||||
if (registers.at(r).name == name)
|
||||
return setRegisterValueU64(ireg, r, value, errorMessage);
|
||||
*errorMessage = QString::fromLatin1("Unable to set register '%1' to %2: No such register")
|
||||
.arg(name).arg(value);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Output parser for disassembler lines: Parse a disassembler line:
|
||||
* \code
|
||||
module!class::foo:
|
||||
004017cf cc int 3
|
||||
77 mainwindow.cpp 004018ff 8d4da8 lea ecx,[ebp-0x58]
|
||||
\endcode
|
||||
* and reformat to something like:
|
||||
* \code
|
||||
00000001400043c9 mainwindow.cpp+296 90 nop
|
||||
00000001400043ca mainwindow.cpp+296 488d8c24d8020000 lea rcx,[rsp+2D8h]
|
||||
00000001400043d2 mainwindow.cpp+296 ff1500640300 call qword ptr [gitgui!_imp_??1QStringQEAAXZ (00000001`4003a7d8)]
|
||||
\endcode
|
||||
* Reformatting brings address to the front for disassembler agent's extracting
|
||||
* the address for location marker to work.
|
||||
* Moves symbol information to the 2nd column, using the source file lines as
|
||||
* symbol until it encounters a C++ symbol (function entered), from which then on
|
||||
* it uses that symbol, indicating the offset.
|
||||
*/
|
||||
|
||||
class DisassemblerOutputParser
|
||||
{
|
||||
Q_DISABLE_COPY(DisassemblerOutputParser)
|
||||
public:
|
||||
explicit DisassemblerOutputParser(QTextStream &str, int addressFieldWidth = 0);
|
||||
|
||||
void parse(const QStringList &l);
|
||||
|
||||
private:
|
||||
enum ParseResult { ParseOk, ParseIgnore, ParseFailed };
|
||||
ParseResult parseDisassembled(const QString &in);
|
||||
|
||||
const int m_addressFieldWidth;
|
||||
QTextStream &m_str;
|
||||
QString m_sourceSymbol;
|
||||
int m_sourceSymbolOffset;
|
||||
};
|
||||
|
||||
DisassemblerOutputParser::DisassemblerOutputParser(QTextStream &str, int addressFieldWidth) :
|
||||
m_addressFieldWidth(addressFieldWidth),
|
||||
m_str(str),
|
||||
m_sourceSymbolOffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
DisassemblerOutputParser::ParseResult
|
||||
DisassemblerOutputParser::parseDisassembled(const QString &in)
|
||||
{
|
||||
// Check if there is a source file
|
||||
if (in.size() < 7)
|
||||
return ParseIgnore;
|
||||
const bool hasSourceFile = !in.at(6).isSpace();
|
||||
|
||||
// Sometimes, empty lines occur
|
||||
const QString simplified = in.simplified();
|
||||
if (simplified.isEmpty())
|
||||
return ParseIgnore;
|
||||
|
||||
const QStringList tokens = simplified.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||
const int tokenCount = tokens.size();
|
||||
// Check for symbols as 'module!class::foo:' (start of function encountered)
|
||||
// and store as state.
|
||||
if (tokenCount == 1) {
|
||||
QString symbol = tokens.front();
|
||||
if (symbol.endsWith(QLatin1Char(':')) && symbol.contains(QLatin1Char('!'))) {
|
||||
symbol.truncate(symbol.size() - 1);
|
||||
m_sourceSymbol = symbol;
|
||||
m_sourceSymbolOffset = 0;
|
||||
}
|
||||
return ParseIgnore;
|
||||
}
|
||||
if (tokenCount < 2)
|
||||
return ParseIgnore;
|
||||
if (tokenCount < 3)
|
||||
return ParseFailed;
|
||||
// Format line. Start with address which is important for setting the marker.
|
||||
// Fix CDB word separator for location marker hex conversion to work.
|
||||
const int addressTokenPos = hasSourceFile ? 2 : 0;
|
||||
QString addressToken = tokens.at(addressTokenPos);
|
||||
if (addressToken.size() > 9 && addressToken.at(8) == QLatin1Char('`'))
|
||||
addressToken.remove(8, 1);
|
||||
m_str << addressToken << ' ';
|
||||
// Symbol display: Do we know a symbol? -> Display with offset.
|
||||
// Else default to source file information.
|
||||
if (m_sourceSymbol.isEmpty()) {
|
||||
if (hasSourceFile)
|
||||
m_str << tokens.at(1) << '+' << tokens.front();
|
||||
} else {
|
||||
m_str << '<' << m_sourceSymbol;
|
||||
if (m_sourceSymbolOffset)
|
||||
m_str << '+' << m_sourceSymbolOffset;
|
||||
m_str << '>';
|
||||
m_sourceSymbolOffset++;
|
||||
}
|
||||
for (int i = addressTokenPos + 1; i < tokenCount; i++)
|
||||
m_str << ' ' << tokens.at(i);
|
||||
m_str << '\n';
|
||||
return ParseOk;
|
||||
}
|
||||
|
||||
void DisassemblerOutputParser::parse(const QStringList &l)
|
||||
{
|
||||
foreach(const QString &line, l) {
|
||||
switch (parseDisassembled(line)) {
|
||||
case ParseOk:
|
||||
case ParseIgnore:
|
||||
break;
|
||||
case ParseFailed:
|
||||
qWarning("Failed to parse '%s'\n", qPrintable(line));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool disassemble(CdbCore::CoreEngine *engine,
|
||||
ULONG64 offset,
|
||||
unsigned long beforeLines,
|
||||
unsigned long afterLines,
|
||||
QTextStream &str,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << offset;
|
||||
QString lines;
|
||||
if (!engine->disassemble(offset, beforeLines, afterLines, &lines, errorMessage))
|
||||
return false;
|
||||
DisassemblerOutputParser parser(str);
|
||||
parser.parse(lines.split(QLatin1Char('\n')));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -1,77 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBASSEMBLER_H
|
||||
#define CDBASSEMBLER_H
|
||||
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include "cdbcom.h"
|
||||
#include "registerhandler.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextStream;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace CdbCore {
|
||||
class CoreEngine;
|
||||
}
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
// Utilities related to assembler code.
|
||||
|
||||
Registers getRegisters(CIDebugControl *ctl,
|
||||
CIDebugRegisters *ireg,
|
||||
QString *errorMessage,
|
||||
int base = 10 /* 16 for hex, etc */);
|
||||
|
||||
bool setRegisterValueU64(CIDebugRegisters *ireg, unsigned index, quint64 value,
|
||||
QString *errorMessage);
|
||||
|
||||
bool setRegisterValueU64(CIDebugControl *ctl, CIDebugRegisters *ireg, const QString &name,
|
||||
quint64 value, QString *errorMessage);
|
||||
|
||||
bool disassemble(CdbCore::CoreEngine *engine,
|
||||
ULONG64 offset,
|
||||
unsigned long beforeLines,
|
||||
unsigned long afterLines,
|
||||
QTextStream &str,
|
||||
QString *errorMessage);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // CDBASSEMBLER_H
|
||||
@@ -1,149 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbbreakpoint.h"
|
||||
#include "cdbengine_p.h"
|
||||
#include "corebreakpoint.h"
|
||||
#include "cdbmodules.h"
|
||||
#include "breakhandler.h"
|
||||
#include "shared/dbgwinutils.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDir>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
// Convert breakpoint structs
|
||||
static CdbCore::BreakPoint breakPointFromBreakPointData(const BreakpointParameters &bpd, const QString &functionName)
|
||||
{
|
||||
CdbCore::BreakPoint rc;
|
||||
rc.type = bpd.type == Watchpoint ?
|
||||
CdbCore::BreakPoint::Data :
|
||||
CdbCore::BreakPoint::Code ;
|
||||
|
||||
rc.address = bpd.address;
|
||||
rc.threadId = bpd.threadSpec;
|
||||
rc.fileName = QDir::toNativeSeparators(bpd.fileName);
|
||||
rc.condition = bpd.condition;
|
||||
rc.funcName = functionName.isEmpty() ? bpd.functionName : functionName;
|
||||
rc.ignoreCount = bpd.ignoreCount;
|
||||
rc.lineNumber = bpd.lineNumber;
|
||||
rc.oneShot = false;
|
||||
rc.enabled = bpd.enabled;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline QString msgCannotSetBreakAtFunction(const QString &func, const QString &why)
|
||||
{
|
||||
return QString::fromLatin1("Cannot set a breakpoint at '%1': %2").arg(func, why);
|
||||
}
|
||||
|
||||
bool addCdbBreakpoint(CIDebugControl* debugControl,
|
||||
CIDebugSymbols *syms,
|
||||
const BreakpointParameters &bpIn,
|
||||
BreakpointResponse *response,
|
||||
QString *errorMessage)
|
||||
{
|
||||
const BreakpointParameters bp = fixWinMSVCBreakpoint(bpIn);
|
||||
errorMessage->clear();
|
||||
// Function breakpoints: Are the module names specified?
|
||||
QString resolvedFunction;
|
||||
if (bp.type == BreakpointByFunction) {
|
||||
resolvedFunction = bp.functionName;
|
||||
switch (resolveSymbol(syms, &resolvedFunction, errorMessage)) {
|
||||
case ResolveSymbolOk:
|
||||
break;
|
||||
case ResolveSymbolAmbiguous:
|
||||
break;
|
||||
case ResolveSymbolNotFound:
|
||||
case ResolveSymbolError:
|
||||
*errorMessage = msgCannotSetBreakAtFunction(bp.functionName, *errorMessage);
|
||||
return false;
|
||||
}
|
||||
if (debugBreakpoints)
|
||||
qDebug() << bp.functionName << " resolved to " << resolvedFunction;
|
||||
} // function breakpoint
|
||||
// Now add...
|
||||
quint64 address;
|
||||
unsigned long id;
|
||||
const CdbCore::BreakPoint ncdbbp = breakPointFromBreakPointData(bp, resolvedFunction);
|
||||
if (!ncdbbp.add(debugControl, errorMessage, &id, &address))
|
||||
return false;
|
||||
if (debugBreakpoints)
|
||||
qDebug("Added %lu at 0x%lx %s", id, address, qPrintable(ncdbbp.toString()));
|
||||
|
||||
response->fromParameters(bp);
|
||||
response->number = id;
|
||||
response->address = address;
|
||||
response->functionName = resolvedFunction;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Delete all breakpoints
|
||||
bool deleteCdbBreakpoints(CIDebugControl* debugControl,
|
||||
QString *errorMessage)
|
||||
{
|
||||
errorMessage->clear();
|
||||
// Do an initial check whether we are in a state that allows
|
||||
// for modifying breakPoints
|
||||
ULONG engineCount;
|
||||
if (!CdbCore::BreakPoint::getBreakPointCount(debugControl, &engineCount, errorMessage)) {
|
||||
*errorMessage = QString::fromLatin1("Cannot modify breakpoints: %1").arg(*errorMessage);
|
||||
return false;
|
||||
}
|
||||
if (debugBreakpoints)
|
||||
qDebug("Deleting breakpoints 0..%lu", engineCount);
|
||||
|
||||
if (engineCount) {
|
||||
for (int b = engineCount - 1; b >= 0 ; b--)
|
||||
if (!CdbCore::BreakPoint::removeBreakPointById(debugControl, b, errorMessage))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void debugCdbBreakpoints(CIDebugControl* debugControl)
|
||||
{
|
||||
QString errorMessage;
|
||||
QList<CdbCore::BreakPoint> bps;
|
||||
CdbCore::BreakPoint::getBreakPoints(debugControl, &bps, &errorMessage);
|
||||
QDebug nsp = qDebug().nospace();
|
||||
const int count = bps.size();
|
||||
nsp <<"### Breakpoints in engine: " << count << '\n';
|
||||
for (int i = 0; i < count; i++)
|
||||
nsp << " #" << i << ' ' << bps.at(i) << '\n';
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -1,64 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBBREAKPOINTS_H
|
||||
#define CDBBREAKPOINTS_H
|
||||
|
||||
#include "cdbcom.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QDebug;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
class BreakpointParameters;
|
||||
class BreakpointResponse;
|
||||
|
||||
// Convert breakpoint structs
|
||||
bool addCdbBreakpoint(CIDebugControl* debugControl,
|
||||
CIDebugSymbols *syms,
|
||||
const BreakpointParameters &bp,
|
||||
BreakpointResponse *response,
|
||||
QString *errorMessage);
|
||||
|
||||
bool deleteCdbBreakpoints(CIDebugControl* debugControl, QString *errorMessage);
|
||||
|
||||
void debugCdbBreakpoints(CIDebugControl* debugControl);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // CDBBREAKPOINTS_H
|
||||
@@ -1,62 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBCOM_H
|
||||
#define CDBCOM_H
|
||||
|
||||
#include <QtCore/QtGlobal>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#include <windows.h>
|
||||
#include <inc/dbgeng.h>
|
||||
|
||||
// typedef out the version numbers
|
||||
typedef IDebugClient5 CIDebugClient;
|
||||
typedef IDebugControl4 CIDebugControl;
|
||||
typedef IDebugSystemObjects4 CIDebugSystemObjects;
|
||||
typedef IDebugSymbols3 CIDebugSymbols;
|
||||
typedef IDebugRegisters2 CIDebugRegisters;
|
||||
typedef IDebugDataSpaces4 CIDebugDataSpaces;
|
||||
|
||||
typedef IDebugSymbolGroup2 CIDebugSymbolGroup;
|
||||
typedef IDebugBreakpoint2 CIDebugBreakpoint;
|
||||
typedef IDebugAdvanced2 CIDebugAdvanced;
|
||||
|
||||
#else
|
||||
|
||||
#include "cdbcomstub.h"
|
||||
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
#endif // CDBCOM_H
|
||||
@@ -1,137 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBCOMSTUB_H
|
||||
#define CDBCOMSTUB_H
|
||||
|
||||
// Stubs to make it partially compile for test purposes on non-Windows.
|
||||
|
||||
// FIXME: Make everything more Windows-like instead of choosing arbitrary
|
||||
// values to make it compile.
|
||||
|
||||
typedef unsigned long ULONG;
|
||||
typedef unsigned long long ULONG64;
|
||||
typedef void *PVOID;
|
||||
typedef unsigned short *PCWSTR;
|
||||
typedef void *HANDLE;
|
||||
typedef int HRESULT;
|
||||
typedef int DEBUG_VALUE;
|
||||
typedef int PDEBUG_BREAKPOINT2;
|
||||
const int MAX_PATH = 1024;
|
||||
|
||||
inline bool FAILED(HRESULT) { return false; }
|
||||
|
||||
enum
|
||||
{
|
||||
DEBUG_OUTPUT_PROMPT_REGISTERS = 1,
|
||||
DEBUG_OUTPUT_EXTENSION_WARNING = 2,
|
||||
DEBUG_OUTPUT_WARNING = 4,
|
||||
DEBUG_OUTPUT_ERROR = 8,
|
||||
DEBUG_OUTPUT_DEBUGGEE = 16,
|
||||
DEBUG_OUTPUT_DEBUGGEE_PROMPT = 32,
|
||||
DEBUG_OUTPUT_PROMPT = 64,
|
||||
};
|
||||
|
||||
#define IN
|
||||
#define OUT
|
||||
#define THIS
|
||||
#define THIS_
|
||||
#define REFIID void *
|
||||
#define THIS_
|
||||
#define STDMETHOD(x) HRESULT x
|
||||
#define STDMETHOD_(x, y) x y
|
||||
|
||||
struct IUnknown
|
||||
{
|
||||
virtual ~IUnknown();
|
||||
virtual STDMETHOD_(ULONG, AddRef)(THIS) { return 1; }
|
||||
virtual STDMETHOD_(ULONG, Release)(THIS) { return 1; }
|
||||
};
|
||||
|
||||
struct IDebugOutputCallbacksWide : IUnknown
|
||||
{
|
||||
};
|
||||
|
||||
struct CIDebugClient : IUnknown
|
||||
{
|
||||
};
|
||||
|
||||
struct CIDebugControl : IUnknown
|
||||
{
|
||||
};
|
||||
|
||||
struct CIDebugSystemObjects : IUnknown
|
||||
{
|
||||
};
|
||||
|
||||
struct CIDebugSymbols : IUnknown
|
||||
{
|
||||
};
|
||||
|
||||
struct CIDebugRegisters : IUnknown
|
||||
{
|
||||
HRESULT GetNumberRegisters(ULONG *) const { return 0; }
|
||||
HRESULT GetDescription(ULONG, char *, int, int, int) const { return 0; }
|
||||
HRESULT GetValues(ULONG, int, int, DEBUG_VALUE *) const { return 0; }
|
||||
};
|
||||
|
||||
struct CIDebugDataSpaces : IUnknown
|
||||
{
|
||||
};
|
||||
|
||||
struct CIDebugSymbolGroup : IUnknown
|
||||
{
|
||||
};
|
||||
|
||||
struct CIDebugBreakpoint : IUnknown
|
||||
{
|
||||
};
|
||||
|
||||
enum DebugSymbolFlags
|
||||
{
|
||||
DEBUG_SYMBOL_IS_LOCAL = 1,
|
||||
DEBUG_SYMBOL_IS_ARGUMENT = 2,
|
||||
DEBUG_SYMBOL_READ_ONLY = 4
|
||||
};
|
||||
|
||||
struct DEBUG_SYMBOL_PARAMETERS
|
||||
{
|
||||
DebugSymbolFlags Flags;
|
||||
unsigned long ParentSymbol;
|
||||
};
|
||||
|
||||
struct DEBUG_STACK_FRAME
|
||||
{
|
||||
};
|
||||
|
||||
#endif // Q_OS_WIN
|
||||
@@ -1,54 +0,0 @@
|
||||
# Detect presence of "Debugging Tools For Windows"
|
||||
# in case VS compilers are used.
|
||||
|
||||
# FIXME
|
||||
CDB_PATH=""
|
||||
win32 {
|
||||
contains(QMAKE_CXX, cl) {
|
||||
|
||||
CDB_PATH="$$(CDB_PATH)"
|
||||
isEmpty(CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows/sdk"
|
||||
|
||||
!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x86)/sdk"
|
||||
!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x64)/sdk"
|
||||
!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows 64-bit/sdk"
|
||||
|
||||
exists($$CDB_PATH) {
|
||||
|
||||
message("Adding support for $$CDB_PATH")
|
||||
|
||||
DEFINES+=CDB_ENABLED
|
||||
|
||||
CDB_PLATFORM=i386
|
||||
|
||||
INCLUDEPATH*=$$CDB_PATH
|
||||
CDB_LIBPATH=$$CDB_PATH/lib/$$CDB_PLATFORM
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/cdbcom.h \
|
||||
$$PWD/coreengine.h \
|
||||
$$PWD/debugoutputbase.h \
|
||||
$$PWD/debugeventcallbackbase.h \
|
||||
$$PWD/symbolgroupcontext.h \
|
||||
$$PWD/stacktracecontext.h \
|
||||
$$PWD/corebreakpoint.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/coreengine.cpp \
|
||||
$$PWD/debugoutputbase.cpp \
|
||||
$$PWD/debugeventcallbackbase.cpp \
|
||||
$$PWD/symbolgroupcontext.cpp \
|
||||
$$PWD/stacktracecontext.cpp \
|
||||
$$PWD/corebreakpoint.cpp
|
||||
|
||||
INCLUDEPATH*=$$PWD
|
||||
DEPENDPATH*=$$PWD
|
||||
|
||||
LIBS+=-lpsapi
|
||||
|
||||
} else {
|
||||
message("Debugging Tools for Windows could not be found in $$CDB_PATH")
|
||||
CDB_PATH=""
|
||||
} # exists($$CDB_PATH)
|
||||
} # (QMAKE_CXX, cl)
|
||||
} # win32
|
||||
@@ -1,261 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbdebugeventcallback.h"
|
||||
#include "cdbengine.h"
|
||||
#include "cdbexceptionutils.h"
|
||||
#include "cdbengine_p.h"
|
||||
#include "dbgwinutils.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
// ---------- CdbDebugEventCallback
|
||||
|
||||
CdbDebugEventCallback::CdbDebugEventCallback(CdbEngine *dbg) :
|
||||
m_pEngine(dbg)
|
||||
{
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbDebugEventCallback::GetInterestMask(THIS_ __out PULONG mask)
|
||||
{
|
||||
*mask = DEBUG_EVENT_CREATE_PROCESS | DEBUG_EVENT_EXIT_PROCESS
|
||||
| DEBUG_EVENT_CREATE_THREAD | DEBUG_EVENT_EXIT_THREAD
|
||||
| DEBUG_EVENT_BREAKPOINT
|
||||
| DEBUG_EVENT_EXCEPTION | baseInterestMask();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbDebugEventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2 Bp)
|
||||
{
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
m_pEngine->m_d->handleBreakpointEvent(Bp);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CdbDebugEventCallback::Exception(
|
||||
THIS_
|
||||
__in PEXCEPTION_RECORD64 Exception,
|
||||
__in ULONG /* FirstChance */
|
||||
)
|
||||
{
|
||||
QString msg;
|
||||
{
|
||||
QTextStream str(&msg);
|
||||
formatException(Exception, &m_pEngine->m_d->interfaces(), str);
|
||||
}
|
||||
const bool fatal = isFatalWinException(Exception->ExceptionCode);
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << "\nex=" << Exception->ExceptionCode << " fatal=" << fatal << msg;
|
||||
m_pEngine->showMessage(msg, AppError);
|
||||
m_pEngine->showMessage(msg, LogMisc);
|
||||
m_pEngine->m_d->notifyException(Exception->ExceptionCode, fatal, msg);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbDebugEventCallback::CreateThread(
|
||||
THIS_
|
||||
__in ULONG64 Handle,
|
||||
__in ULONG64 DataOffset,
|
||||
__in ULONG64 StartOffset
|
||||
)
|
||||
{
|
||||
Q_UNUSED(Handle)
|
||||
Q_UNUSED(DataOffset)
|
||||
Q_UNUSED(StartOffset)
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
m_pEngine->m_d->updateThreadList();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbDebugEventCallback::ExitThread(
|
||||
THIS_
|
||||
__in ULONG ExitCode
|
||||
)
|
||||
{
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << ExitCode;
|
||||
// @TODO: It seems the terminated thread is still in the list...
|
||||
m_pEngine->m_d->updateThreadList();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbDebugEventCallback::CreateProcess(
|
||||
THIS_
|
||||
__in ULONG64 ImageFileHandle,
|
||||
__in ULONG64 Handle,
|
||||
__in ULONG64 BaseOffset,
|
||||
__in ULONG ModuleSize,
|
||||
__in_opt PCWSTR ModuleName,
|
||||
__in_opt PCWSTR ImageName,
|
||||
__in ULONG CheckSum,
|
||||
__in ULONG TimeDateStamp,
|
||||
__in ULONG64 InitialThreadHandle,
|
||||
__in ULONG64 ThreadDataOffset,
|
||||
__in ULONG64 StartOffset
|
||||
)
|
||||
{
|
||||
Q_UNUSED(ImageFileHandle)
|
||||
Q_UNUSED(BaseOffset)
|
||||
Q_UNUSED(ModuleSize)
|
||||
Q_UNUSED(ModuleName)
|
||||
Q_UNUSED(ImageName)
|
||||
Q_UNUSED(CheckSum)
|
||||
Q_UNUSED(TimeDateStamp)
|
||||
Q_UNUSED(ThreadDataOffset)
|
||||
Q_UNUSED(StartOffset)
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << ModuleName;
|
||||
m_pEngine->m_d->processCreatedAttached(Handle, InitialThreadHandle);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbDebugEventCallback::ExitProcess(
|
||||
THIS_
|
||||
__in ULONG ExitCode
|
||||
)
|
||||
{
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << ExitCode;
|
||||
m_pEngine->processTerminated(ExitCode);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbDebugEventCallback::LoadModule(
|
||||
THIS_
|
||||
__in ULONG64 ImageFileHandle,
|
||||
__in ULONG64 BaseOffset,
|
||||
__in ULONG ModuleSize,
|
||||
__in_opt PCWSTR ModuleName,
|
||||
__in_opt PCWSTR ImageName,
|
||||
__in ULONG CheckSum,
|
||||
__in ULONG TimeDateStamp
|
||||
)
|
||||
{
|
||||
Q_UNUSED(ImageFileHandle)
|
||||
Q_UNUSED(BaseOffset)
|
||||
Q_UNUSED(ModuleSize)
|
||||
Q_UNUSED(ImageName)
|
||||
Q_UNUSED(CheckSum)
|
||||
Q_UNUSED(TimeDateStamp)
|
||||
if (debugCDB > 1)
|
||||
qDebug() << Q_FUNC_INFO << ModuleName;
|
||||
handleModuleLoad();
|
||||
m_pEngine->m_d->handleModuleLoad(BaseOffset, QString::fromUtf16(reinterpret_cast<const ushort *>(ModuleName)));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbDebugEventCallback::UnloadModule(
|
||||
THIS_
|
||||
__in_opt PCWSTR ImageBaseName,
|
||||
__in ULONG64 BaseOffset
|
||||
)
|
||||
{
|
||||
Q_UNUSED(BaseOffset)
|
||||
if (debugCDB > 1)
|
||||
qDebug() << Q_FUNC_INFO << ImageBaseName;
|
||||
if (!ImageBaseName)
|
||||
return S_OK;
|
||||
m_pEngine->m_d->handleModuleUnload(QString::fromUtf16(reinterpret_cast<const ushort *>(ImageBaseName)));
|
||||
handleModuleUnload();
|
||||
m_pEngine->m_d->updateModules();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbDebugEventCallback::SystemError(
|
||||
THIS_
|
||||
__in ULONG Error,
|
||||
__in ULONG Level
|
||||
)
|
||||
{
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << Error << Level;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// -----------ExceptionLoggerEventCallback
|
||||
CdbExceptionLoggerEventCallback::CdbExceptionLoggerEventCallback(int logChannel,
|
||||
bool skipNonFatalExceptions, CdbEngine *engine) :
|
||||
m_logChannel(logChannel),
|
||||
m_skipNonFatalExceptions(skipNonFatalExceptions),
|
||||
m_engine(engine)
|
||||
{
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbExceptionLoggerEventCallback::GetInterestMask(THIS_ __out PULONG mask)
|
||||
{
|
||||
*mask = DEBUG_EVENT_EXCEPTION | baseInterestMask();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbExceptionLoggerEventCallback::Exception(
|
||||
THIS_
|
||||
__in PEXCEPTION_RECORD64 Exception,
|
||||
__in ULONG /* FirstChance */
|
||||
)
|
||||
{
|
||||
const bool recordException = !m_skipNonFatalExceptions || isFatalWinException(Exception->ExceptionCode);
|
||||
QString msg;
|
||||
formatException(Exception, QTextStream(&msg));
|
||||
if (recordException) {
|
||||
m_exceptionCodes.push_back(Exception->ExceptionCode);
|
||||
m_exceptionMessages.push_back(msg);
|
||||
}
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << '\n' << msg;
|
||||
m_engine->showMessage(msg, m_logChannel);
|
||||
if (recordException)
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// -----------IgnoreDebugEventCallback
|
||||
IgnoreDebugEventCallback::IgnoreDebugEventCallback()
|
||||
{
|
||||
}
|
||||
|
||||
STDMETHODIMP IgnoreDebugEventCallback::GetInterestMask(THIS_ __out PULONG mask)
|
||||
{
|
||||
*mask = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -1,174 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef DEBUGGER_CDBDEBUGEVENTCALLBACK_H
|
||||
#define DEBUGGER_CDBDEBUGEVENTCALLBACK_H
|
||||
|
||||
#include "debugeventcallbackbase.h"
|
||||
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class CdbEngine;
|
||||
|
||||
class CdbDebugEventCallback : public CdbCore::DebugEventCallbackBase
|
||||
{
|
||||
public:
|
||||
explicit CdbDebugEventCallback(CdbEngine* dbg);
|
||||
|
||||
// IDebugEventCallbacks.
|
||||
|
||||
STDMETHOD(GetInterestMask)(
|
||||
THIS_
|
||||
__out PULONG mask
|
||||
);
|
||||
|
||||
STDMETHOD(Breakpoint)(
|
||||
THIS_
|
||||
__in PDEBUG_BREAKPOINT2 Bp
|
||||
);
|
||||
|
||||
STDMETHOD(Exception)(
|
||||
THIS_
|
||||
__in PEXCEPTION_RECORD64 Exception,
|
||||
__in ULONG FirstChance
|
||||
);
|
||||
|
||||
STDMETHOD(CreateThread)(
|
||||
THIS_
|
||||
__in ULONG64 Handle,
|
||||
__in ULONG64 DataOffset,
|
||||
__in ULONG64 StartOffset
|
||||
);
|
||||
STDMETHOD(ExitThread)(
|
||||
THIS_
|
||||
__in ULONG ExitCode
|
||||
);
|
||||
|
||||
STDMETHOD(CreateProcess)(
|
||||
THIS_
|
||||
__in ULONG64 ImageFileHandle,
|
||||
__in ULONG64 Handle,
|
||||
__in ULONG64 BaseOffset,
|
||||
__in ULONG ModuleSize,
|
||||
__in_opt PCWSTR ModuleName,
|
||||
__in_opt PCWSTR ImageName,
|
||||
__in ULONG CheckSum,
|
||||
__in ULONG TimeDateStamp,
|
||||
__in ULONG64 InitialThreadHandle,
|
||||
__in ULONG64 ThreadDataOffset,
|
||||
__in ULONG64 StartOffset
|
||||
);
|
||||
|
||||
STDMETHOD(ExitProcess)(
|
||||
THIS_
|
||||
__in ULONG ExitCode
|
||||
);
|
||||
|
||||
STDMETHOD(LoadModule)(
|
||||
THIS_
|
||||
__in ULONG64 ImageFileHandle,
|
||||
__in ULONG64 BaseOffset,
|
||||
__in ULONG ModuleSize,
|
||||
__in_opt PCWSTR ModuleName,
|
||||
__in_opt PCWSTR ImageName,
|
||||
__in ULONG CheckSum,
|
||||
__in ULONG TimeDateStamp
|
||||
);
|
||||
|
||||
STDMETHOD(UnloadModule)(
|
||||
THIS_
|
||||
__in_opt PCWSTR ImageBaseName,
|
||||
__in ULONG64 BaseOffset
|
||||
);
|
||||
|
||||
STDMETHOD(SystemError)(
|
||||
THIS_
|
||||
__in ULONG Error,
|
||||
__in ULONG Level
|
||||
);
|
||||
|
||||
private:
|
||||
CdbEngine *m_pEngine;
|
||||
};
|
||||
|
||||
// Event handler logs exceptions to the debugger window
|
||||
// and ignores the rest. To be used for running dumper calls.
|
||||
class CdbExceptionLoggerEventCallback : public CdbCore::DebugEventCallbackBase
|
||||
{
|
||||
public:
|
||||
CdbExceptionLoggerEventCallback(int logChannel,
|
||||
bool skipNonFatalExceptions,
|
||||
CdbEngine *engine);
|
||||
|
||||
STDMETHOD(GetInterestMask)(
|
||||
THIS_
|
||||
__out PULONG mask
|
||||
);
|
||||
|
||||
STDMETHOD(Exception)(
|
||||
THIS_
|
||||
__in PEXCEPTION_RECORD64 Exception,
|
||||
__in ULONG FirstChance
|
||||
);
|
||||
|
||||
int exceptionCount() const { return m_exceptionMessages.size(); }
|
||||
QStringList exceptionMessages() const { return m_exceptionMessages; }
|
||||
QList<ULONG> exceptionCodes() const { return m_exceptionCodes; }
|
||||
|
||||
private:
|
||||
const int m_logChannel;
|
||||
const bool m_skipNonFatalExceptions;
|
||||
CdbEngine *m_engine;
|
||||
QList<ULONG> m_exceptionCodes;
|
||||
QStringList m_exceptionMessages;
|
||||
};
|
||||
|
||||
// Event handler that ignores everything
|
||||
class IgnoreDebugEventCallback : public CdbCore::DebugEventCallbackBase
|
||||
{
|
||||
public:
|
||||
explicit IgnoreDebugEventCallback();
|
||||
|
||||
STDMETHOD(GetInterestMask)(
|
||||
THIS_
|
||||
__out PULONG mask
|
||||
);
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // DEBUGGER_CDBDEBUGEVENTCALLBACK_H
|
||||
@@ -1,79 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbdebugoutput.h"
|
||||
#include "debuggerrunner.h"
|
||||
#include "cdbengine_p.h"
|
||||
#include "cdbcom.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
// ------------------------- CdbDebugOutput
|
||||
|
||||
// Return a prefix for debugger messages
|
||||
static int logChannel(ULONG mask)
|
||||
{
|
||||
if (mask & (DEBUG_OUTPUT_PROMPT_REGISTERS))
|
||||
return LogMisc;
|
||||
if (mask & (DEBUG_OUTPUT_EXTENSION_WARNING|DEBUG_OUTPUT_WARNING))
|
||||
return LogWarning;
|
||||
if (mask & (DEBUG_OUTPUT_ERROR))
|
||||
return LogError;
|
||||
if (mask & DEBUG_OUTPUT_DEBUGGEE)
|
||||
//return DebuggeeOutput;
|
||||
return AppOutput;
|
||||
if (mask & DEBUG_OUTPUT_DEBUGGEE_PROMPT)
|
||||
//return DebuggeePromptOutput;
|
||||
return AppError;
|
||||
if (mask & DEBUG_OUTPUT_PROMPT)
|
||||
//return DebuggerPromptOutput;
|
||||
return AppError;
|
||||
return LogMisc;
|
||||
}
|
||||
|
||||
CdbDebugOutput::CdbDebugOutput()
|
||||
{
|
||||
}
|
||||
|
||||
void CdbDebugOutput::output(ULONG mask, const QString &msg)
|
||||
{
|
||||
if (debugCDB > 1)
|
||||
qDebug() << Q_FUNC_INFO << "\n " << msg;
|
||||
emit showMessage(msg, logChannel(mask), -1);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -1,61 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef DEBUGGER_CDBOUTPUT_H
|
||||
#define DEBUGGER_CDBOUTPUT_H
|
||||
|
||||
#include "debugoutputbase.h"
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
// Standard CDB output handler
|
||||
class CdbDebugOutput : public QObject, public CdbCore::DebugOutputBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CdbDebugOutput();
|
||||
|
||||
signals:
|
||||
void showMessage(const QString &output, int channel, int timeout);
|
||||
|
||||
protected:
|
||||
virtual void output(ULONG mask, const QString &message);
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // DEBUGGER_CDBOUTPUT_H
|
||||
@@ -1,801 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbdumperhelper.h"
|
||||
#include "cdbmodules.h"
|
||||
#include "cdbengine.h"
|
||||
#include "cdbengine_p.h"
|
||||
#include "cdbdebugoutput.h"
|
||||
#include "cdbdebugeventcallback.h"
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
#include "watchhandler.h"
|
||||
#include "cdbexceptionutils.h"
|
||||
|
||||
#include "shared/sharedlibraryinjector.h"
|
||||
|
||||
#include <QtCore/QRegExp>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QTime>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QEventLoop>
|
||||
#include <QtGui/QApplication>
|
||||
|
||||
enum { loadDebug = 0 };
|
||||
enum { dumpDebug = 0 };
|
||||
|
||||
static const char *dumperModuleNameC = "gdbmacros";
|
||||
static const char *qtCoreModuleNameC = "QtCore";
|
||||
static const ULONG waitTimeOutMS = 30000;
|
||||
static const char *dumperPrefixC = "dumper:";
|
||||
|
||||
/* Loading the dumpers is 2 step process:
|
||||
* 1) The library must be loaded into the debuggee, for which there are
|
||||
* 2 approaches:
|
||||
* - Injection loading using the SharedLibraryInjector which
|
||||
* launches a remote thread in the debuggee which loads the
|
||||
* library. Drawbacks:
|
||||
* * The remote thread must not starve.
|
||||
* * It is not possible to wait loading and loading occurs late,
|
||||
* after entering main()
|
||||
* - Debugger Call loading, which has the debuggee execute
|
||||
* a LoadLibrary call via debugger commands. Drawbacks:
|
||||
* * Slow
|
||||
* * Requires presence of a symbol of the same prototype as
|
||||
* LoadLibraryA as the original prototype is not sufficient.
|
||||
* 2) Call a query function (protocol 1 of the dumper) to obtain a list
|
||||
* of handled types and a map of known sizes.
|
||||
*
|
||||
* The class currently launches injection loading from the module
|
||||
* load hook as soon as it sees a Qt module.
|
||||
* The dumpType() function performs the rest of the [delayed] initialization.
|
||||
* If the load has not finished, it attempts call loading and
|
||||
* executes the initial query protocol.
|
||||
*
|
||||
* Note: The main technique here is having the debuggee call functions
|
||||
* using the .call command (which takes a function with a known
|
||||
* prototype and simple, integer parameters).
|
||||
* This does not work from an IDebugEvent callback, as it will cause
|
||||
* WaitForEvent() to fail with unknown errors.
|
||||
* It mostly works from breakpoints, with the addditional restriction
|
||||
* that complex functions only work from 'well-defined' breakpoints
|
||||
* (such as main()) and otherwise cause access violation exceptions
|
||||
* (for example LoadLibrary).
|
||||
* Exceptions occurring in the functions to be called must be handled
|
||||
* by __try/__except (they show up in the debugger and must acknowledged
|
||||
* by gN (go not handled). */
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
// ------- Call load helpers
|
||||
|
||||
// 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(CdbEngine *cdbEngine,
|
||||
CdbCore::CoreEngine *engine,
|
||||
unsigned long threadId,
|
||||
const QString &moduleName,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (loadDebug > 1)
|
||||
qDebug() << Q_FUNC_INFO << moduleName;
|
||||
// Try to ignore the breakpoints, skip stray startup-complete trap exceptions
|
||||
QSharedPointer<CdbExceptionLoggerEventCallback>
|
||||
exLogger(new CdbExceptionLoggerEventCallback(LogWarning, true, cdbEngine));
|
||||
CdbCore::EventCallbackRedirector eventRedir(engine, exLogger);
|
||||
Q_UNUSED(eventRedir)
|
||||
// Make a call to LoadLibraryA. First, reserve memory in debugger
|
||||
// and copy name over.
|
||||
ULONG64 nameAddress;
|
||||
if (!engine->createDebuggeeAscIIString(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.
|
||||
// Call with a prototype of 'qstrdup', as it is the same
|
||||
// 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(engine->interfaces().debugSymbols, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
|
||||
return false;
|
||||
|
||||
QString callCmd; {
|
||||
QTextStream str(&callCmd);
|
||||
str.setIntegerBase(16);
|
||||
str << ".call /s " << dummyFunc << " Kernel32!LoadLibraryA(0x" << nameAddress << ')';
|
||||
}
|
||||
if (loadDebug)
|
||||
qDebug() << "Calling" << callCmd;
|
||||
|
||||
if (!engine->executeDebuggerCommand(callCmd, errorMessage))
|
||||
return false;
|
||||
// Execute current thread. This will hit a breakpoint.
|
||||
QString goCmd;
|
||||
QTextStream(&goCmd) << '~' << threadId << " g";
|
||||
if (!engine->executeDebuggerCommand(goCmd, errorMessage))
|
||||
return false;
|
||||
const HRESULT hr = engine->waitForEvent(waitTimeOutMS);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = CdbCore::msgComFailed("WaitForEvent", hr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Format a "go" in a thread
|
||||
static inline QString goCommand(unsigned long threadId)
|
||||
{
|
||||
QString rc;
|
||||
QTextStream(&rc) << '~' << threadId << " g";
|
||||
return rc;
|
||||
}
|
||||
|
||||
// ---- Load messages
|
||||
static inline QString msgMethod(bool injectOrCall)
|
||||
{
|
||||
return injectOrCall ?
|
||||
QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "injection") :
|
||||
QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "debugger call");
|
||||
}
|
||||
|
||||
static QString msgLoading(const QString &library, bool injectOrCall)
|
||||
{
|
||||
return QCoreApplication::translate("Debugger::Internal::CdbDumperHelper",
|
||||
"Loading the custom dumper library '%1' (%2) ...").
|
||||
arg(library, msgMethod(injectOrCall));
|
||||
}
|
||||
|
||||
static QString msgLoadFailed(const QString &library, bool injectOrCall, const QString &why)
|
||||
{
|
||||
return QCoreApplication::translate("Debugger::Internal::CdbDumperHelper",
|
||||
"Loading of the custom dumper library '%1' (%2) failed: %3").
|
||||
arg(library, msgMethod(injectOrCall), why);
|
||||
}
|
||||
|
||||
static QString msgLoadSucceeded(const QString &library, bool injectOrCall)
|
||||
{
|
||||
return QCoreApplication::translate("Debugger::Internal::CdbDumperHelper",
|
||||
"Loaded the custom dumper library '%1' (%2).").
|
||||
arg(library, msgMethod(injectOrCall));
|
||||
}
|
||||
|
||||
// Dumper initialization as a background thread.
|
||||
// Befriends CdbDumperHelper and calls its methods
|
||||
class CdbDumperInitThread : public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static inline bool ensureDumperInitialized(CdbDumperHelper &h, QString *errorMessage);
|
||||
|
||||
virtual void run();
|
||||
|
||||
signals:
|
||||
void logMessage(const QString &m, int channel);
|
||||
void statusMessage(const QString &m, int timeOut);
|
||||
|
||||
private:
|
||||
explicit CdbDumperInitThread(CdbDumperHelper &h, QString *errorMessage);
|
||||
|
||||
CdbDumperHelper &m_helper;
|
||||
bool m_ok;
|
||||
QString *m_errorMessage;
|
||||
};
|
||||
|
||||
CdbDumperInitThread::CdbDumperInitThread(CdbDumperHelper &h, QString *errorMessage) :
|
||||
m_helper(h),
|
||||
m_ok(false),
|
||||
m_errorMessage(errorMessage)
|
||||
{
|
||||
}
|
||||
|
||||
bool CdbDumperInitThread::ensureDumperInitialized(CdbDumperHelper &h, QString *errorMessage)
|
||||
{
|
||||
// Quick state check
|
||||
switch (h.state()) {
|
||||
case CdbDumperHelper::Disabled:
|
||||
*errorMessage = QLatin1String("Internal error, attempt to call disabled dumper");
|
||||
return false;
|
||||
case CdbDumperHelper::Initialized:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Need a thread to do initialization work. Typically
|
||||
// takes several seconds depending on debuggee size.
|
||||
QApplication::setOverrideCursor(Qt::BusyCursor);
|
||||
CdbDumperInitThread thread(h, errorMessage);
|
||||
connect(&thread, SIGNAL(statusMessage(QString,int)),
|
||||
h.m_engine, SLOT(showStatusMessage(QString,int)),
|
||||
Qt::QueuedConnection);
|
||||
connect(&thread, SIGNAL(logMessage(QString,int)),
|
||||
h.m_engine, SLOT(showMessage(QString,int)),
|
||||
Qt::QueuedConnection);
|
||||
QEventLoop eventLoop;
|
||||
connect(&thread, SIGNAL(finished()), &eventLoop, SLOT(quit()), Qt::QueuedConnection);
|
||||
thread.start();
|
||||
if (thread.isRunning())
|
||||
eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
|
||||
QApplication::restoreOverrideCursor();
|
||||
if (thread.m_ok) {
|
||||
h.m_engine->showStatusMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "Stopped / Custom dumper library initialized."), messageTimeOut);
|
||||
h.m_engine->showMessage(h.m_helper.toString());
|
||||
h.m_state = CdbDumperHelper::Initialized;
|
||||
} else {
|
||||
h.m_state = CdbDumperHelper::Disabled; // No message here
|
||||
*errorMessage = QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "The custom dumper library could not be initialized: %1").arg(*errorMessage);
|
||||
h.m_engine->showStatusMessage(*errorMessage, messageTimeOut);
|
||||
h.m_engine->showQtDumperLibraryWarning(*errorMessage);
|
||||
}
|
||||
if (loadDebug)
|
||||
qDebug() << Q_FUNC_INFO << '\n' << thread.m_ok;
|
||||
return thread.m_ok;
|
||||
}
|
||||
|
||||
void CdbDumperInitThread ::run()
|
||||
{
|
||||
switch (m_helper.state()) {
|
||||
// Injection load failed or disabled: Try a call load.
|
||||
case CdbDumperHelper::NotLoaded:
|
||||
case CdbDumperHelper::InjectLoading:
|
||||
case CdbDumperHelper::InjectLoadFailed:
|
||||
// Also shows up in the log window.
|
||||
emit statusMessage(msgLoading(m_helper.m_library, false), -1);
|
||||
switch (m_helper.initCallLoad(m_errorMessage)) {
|
||||
case CdbDumperHelper::CallLoadOk:
|
||||
case CdbDumperHelper::CallLoadAlreadyLoaded:
|
||||
emit logMessage(msgLoadSucceeded(m_helper.m_library, false), LogMisc);
|
||||
m_helper.m_state = CdbDumperHelper::Loaded;
|
||||
break;
|
||||
case CdbDumperHelper::CallLoadError:
|
||||
*m_errorMessage = msgLoadFailed(m_helper.m_library, false, *m_errorMessage);
|
||||
m_ok = false;
|
||||
return;
|
||||
case CdbDumperHelper::CallLoadNoQtApp:
|
||||
emit logMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "The debuggee does not appear to be Qt application."), LogMisc);
|
||||
m_helper.m_state = CdbDumperHelper::Disabled; // No message here
|
||||
m_ok = true;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case CdbDumperHelper::Loaded: // Injection load succeeded, ideally
|
||||
break;
|
||||
}
|
||||
// Perform remaining initialization
|
||||
emit statusMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "Initializing dumpers..."), 60000);
|
||||
m_ok = m_helper.initResolveSymbols(m_errorMessage) && m_helper.initKnownTypes(m_errorMessage);
|
||||
}
|
||||
|
||||
// ------------------- CdbDumperHelper
|
||||
|
||||
CdbDumperHelper::CdbDumperHelper(CdbEngine *engine,
|
||||
CdbCore::CoreEngine *coreEngine) :
|
||||
m_tryInjectLoad(true),
|
||||
m_msgDisabled(QLatin1String("Dumpers are disabled")),
|
||||
m_msgNotInScope(QLatin1String("Data not in scope")),
|
||||
m_state(NotLoaded),
|
||||
m_engine(engine),
|
||||
m_coreEngine(coreEngine),
|
||||
m_inBufferAddress(0),
|
||||
m_inBufferSize(0),
|
||||
m_outBufferAddress(0),
|
||||
m_outBufferSize(0),
|
||||
m_buffer(0),
|
||||
m_dumperCallThread(0),
|
||||
m_goCommand(goCommand(m_dumperCallThread)),
|
||||
m_fastSymbolResolution(true)
|
||||
{
|
||||
}
|
||||
|
||||
CdbDumperHelper::~CdbDumperHelper()
|
||||
{
|
||||
clearBuffer();
|
||||
}
|
||||
|
||||
void CdbDumperHelper::disable()
|
||||
{
|
||||
if (loadDebug)
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
m_engine->showMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "Disabling dumpers due to debuggee crash..."));
|
||||
m_state = Disabled;
|
||||
}
|
||||
|
||||
void CdbDumperHelper::clearBuffer()
|
||||
{
|
||||
if (m_buffer) {
|
||||
delete [] m_buffer;
|
||||
m_buffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CdbDumperHelper::reset(const QString &library, bool enabled)
|
||||
{
|
||||
if (loadDebug)
|
||||
qDebug() << Q_FUNC_INFO << '\n' << library << enabled;
|
||||
m_library = library;
|
||||
m_state = enabled ? NotLoaded : Disabled;
|
||||
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
|
||||
m_helper.clear();
|
||||
m_inBufferAddress = m_outBufferAddress = 0;
|
||||
m_inBufferSize = m_outBufferSize = 0;
|
||||
m_failedTypes.clear();
|
||||
clearBuffer();
|
||||
}
|
||||
|
||||
void CdbDumperHelper::moduleLoadHook(const QString &module, HANDLE debuggeeHandle)
|
||||
{
|
||||
if (loadDebug > 1)
|
||||
qDebug() << "moduleLoadHook" << module << m_state << debuggeeHandle;
|
||||
switch (m_state) {
|
||||
case Disabled:
|
||||
case Initialized:
|
||||
break;
|
||||
case NotLoaded:
|
||||
// Try an inject load as soon as a Qt lib is loaded.
|
||||
// for the thread to finish as this would lock up.
|
||||
if (m_tryInjectLoad && module.contains(QLatin1String("Qt"), Qt::CaseInsensitive)) {
|
||||
// Also shows up in the log window.
|
||||
m_engine->showMessage(msgLoading(m_library, true), StatusBar, messageTimeOut);
|
||||
QString errorMessage;
|
||||
SharedLibraryInjector sh(GetProcessId(debuggeeHandle));
|
||||
if (sh.remoteInject(m_library, false, &errorMessage)) {
|
||||
m_state = InjectLoading;
|
||||
} else {
|
||||
m_state = InjectLoadFailed;
|
||||
// Ok, try call loading...
|
||||
m_engine->showMessage(msgLoadFailed(m_library, true, errorMessage));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case InjectLoading:
|
||||
// check if gdbmacros.dll loaded
|
||||
if (module.contains(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive)) {
|
||||
m_state = Loaded;
|
||||
m_engine->showMessage(msgLoadSucceeded(m_library, true));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to load dumpers by triggering calls using the debugger
|
||||
CdbDumperHelper::CallLoadResult CdbDumperHelper::initCallLoad(QString *errorMessage)
|
||||
{
|
||||
if (loadDebug)
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
// Do we have Qt and are we already loaded by accident?
|
||||
QStringList modules;
|
||||
if (!getModuleNameList(m_coreEngine->interfaces().debugSymbols, &modules, errorMessage))
|
||||
return CallLoadError;
|
||||
// Are we already loaded by some accident?
|
||||
if (!modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty())
|
||||
return CallLoadAlreadyLoaded;
|
||||
// Is that Qt application at all?
|
||||
if (modules.filter(QLatin1String(qtCoreModuleNameC), Qt::CaseInsensitive).isEmpty())
|
||||
return CallLoadNoQtApp;
|
||||
// Try to load
|
||||
if (!debuggeeLoadLibrary(m_engine, m_coreEngine, m_dumperCallThread, m_library, errorMessage))
|
||||
return CallLoadError;
|
||||
return CallLoadOk;
|
||||
}
|
||||
|
||||
// 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(reinterpret_cast<PCWSTR>(name.utf16()), address);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = CdbCore::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 = CdbCore::msgComFailed("GetOffsetTypeId", hr);
|
||||
return false;
|
||||
}
|
||||
hr = sg->GetTypeSize(moduleAddress, type, size);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = CdbCore::msgComFailed("GetTypeSize", hr);
|
||||
return false;
|
||||
}
|
||||
} // size desired
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CdbDumperHelper::initResolveSymbols(QString *errorMessage)
|
||||
{
|
||||
// Resolve the symbols we need.
|
||||
// There is a 'qDumpInBuffer' in QtCore as well.
|
||||
if (loadDebug)
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
const QString dumperModuleName = QLatin1String(dumperModuleNameC);
|
||||
QString inBufferSymbol, outBufferSymbol;
|
||||
bool rc;
|
||||
if (m_fastSymbolResolution) {
|
||||
// Symbols in the debugging helpers are never namespaced.
|
||||
m_dumpObjectSymbol = dumperModuleName + QLatin1String("!qDumpObjectData440");
|
||||
inBufferSymbol = dumperModuleName + QLatin1String("!qDumpInBuffer");
|
||||
outBufferSymbol = dumperModuleName + QLatin1String("!qDumpOutBuffer");
|
||||
} else {
|
||||
// Classical approach of loading the dumper symbols. Takes some time though.
|
||||
m_dumpObjectSymbol = QLatin1String("*qDumpObjectData440");
|
||||
inBufferSymbol = QLatin1String("*qDumpInBuffer");
|
||||
outBufferSymbol = QLatin1String("*qDumpOutBuffer");
|
||||
|
||||
rc = resolveSymbol(m_coreEngine->interfaces().debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk
|
||||
&& resolveSymbol(m_coreEngine->interfaces().debugSymbols, dumperModuleName, &inBufferSymbol, errorMessage) == ResolveSymbolOk
|
||||
&& resolveSymbol(m_coreEngine->interfaces().debugSymbols, dumperModuleName, &outBufferSymbol, errorMessage) == ResolveSymbolOk;
|
||||
if (!rc)
|
||||
return false;
|
||||
}
|
||||
// Determine buffer addresses, sizes and alloc buffer
|
||||
rc = getSymbolAddress(m_coreEngine->interfaces().debugSymbols, inBufferSymbol, &m_inBufferAddress, &m_inBufferSize, errorMessage)
|
||||
&& getSymbolAddress(m_coreEngine->interfaces().debugSymbols, outBufferSymbol, &m_outBufferAddress, &m_outBufferSize, errorMessage);
|
||||
if (!rc)
|
||||
return false;
|
||||
m_buffer = new char[qMax(m_inBufferSize, m_outBufferSize)];
|
||||
if (loadDebug)
|
||||
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;
|
||||
}
|
||||
|
||||
// Call query protocol to retrieve known types and sizes
|
||||
bool CdbDumperHelper::initKnownTypes(QString *errorMessage)
|
||||
{
|
||||
if (loadDebug)
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
const double dumperVersionRequired = 1.3;
|
||||
QByteArray output;
|
||||
QString callCmd;
|
||||
QTextStream(&callCmd) << ".call " << m_dumpObjectSymbol << "(1,0,0,0,0,0,0,0)";
|
||||
const char *outData;
|
||||
if (callDumper(callCmd, QByteArray(), &outData, false, errorMessage) != CallOk) {
|
||||
return false;
|
||||
}
|
||||
if (!m_helper.parseQuery(outData)) {
|
||||
*errorMessage = QString::fromLatin1("Unable to parse the dumper output: '%1'").arg(QString::fromAscii(output));
|
||||
}
|
||||
if (m_helper.dumperVersion() < dumperVersionRequired) {
|
||||
*errorMessage = QtDumperHelper::msgDumperOutdated(dumperVersionRequired, m_helper.dumperVersion());
|
||||
return false;
|
||||
}
|
||||
if (loadDebug || dumpDebug)
|
||||
qDebug() << Q_FUNC_INFO << '\n' << m_helper.toString(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
CdbDumperHelper::CallResult
|
||||
CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr,
|
||||
bool ignoreAccessViolation, QString *errorMessage)
|
||||
{
|
||||
*outDataPtr = 0;
|
||||
// Skip stray startup-complete trap exceptions.
|
||||
QSharedPointer<CdbExceptionLoggerEventCallback> exLogger(new
|
||||
CdbExceptionLoggerEventCallback(LogWarning, true, m_engine));
|
||||
CdbCore::EventCallbackRedirector eventRedir(m_coreEngine, exLogger);
|
||||
Q_UNUSED(eventRedir)
|
||||
// write input buffer
|
||||
if (!inBuffer.isEmpty()) {
|
||||
if (!m_coreEngine->writeToDebuggee(inBuffer, m_inBufferAddress, errorMessage))
|
||||
return CallFailed;
|
||||
}
|
||||
if (!m_coreEngine->executeDebuggerCommand(callCmd, errorMessage)) {
|
||||
// Clear the outstanding call in case we triggered a debug library assert with a message box
|
||||
QString clearError;
|
||||
if (!m_coreEngine->executeDebuggerCommand(QLatin1String(".call /c"), &clearError)) {
|
||||
*errorMessage += QString::fromLatin1("/Unable to clear call %1").arg(clearError);
|
||||
}
|
||||
return CallSyntaxError;
|
||||
}
|
||||
// Set up call and a temporary breakpoint after it.
|
||||
// Try to skip debuggee crash exceptions and dumper exceptions
|
||||
// by using 'gN' (go not handled -> pass handling to dumper __try/__catch block)
|
||||
for (int i = 0; i < 10; i++) {
|
||||
const int oldExceptionCount = exLogger->exceptionCount();
|
||||
// Go in current thread. If an exception occurs in loop 2,
|
||||
// let the dumper handle it.
|
||||
QString goCmd = m_goCommand;
|
||||
if (i)
|
||||
goCmd = QLatin1Char('N');
|
||||
if (!m_coreEngine->executeDebuggerCommand(goCmd, errorMessage))
|
||||
return CallFailed;
|
||||
HRESULT hr = m_coreEngine->waitForEvent(waitTimeOutMS);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = CdbCore::msgComFailed("WaitForEvent", hr);
|
||||
return CallFailed;
|
||||
}
|
||||
const int newExceptionCount = exLogger->exceptionCount();
|
||||
// no new exceptions? -> break
|
||||
if (oldExceptionCount == newExceptionCount)
|
||||
break;
|
||||
// If we are to ignore EXCEPTION_ACCESS_VIOLATION, check if anything
|
||||
// else occurred.
|
||||
if (ignoreAccessViolation) {
|
||||
const QList<ULONG> newExceptionCodes = exLogger->exceptionCodes().mid(oldExceptionCount);
|
||||
if (newExceptionCodes.count(EXCEPTION_ACCESS_VIOLATION) == newExceptionCodes.size())
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exLogger->exceptionCount()) {
|
||||
const QString exMsgs = exLogger->exceptionMessages().join(QString(QLatin1Char(',')));
|
||||
*errorMessage = QString::fromLatin1("Exceptions occurred during the dumper call: %1").arg(exMsgs);
|
||||
return CallFailed;
|
||||
}
|
||||
// Read output
|
||||
const HRESULT hr = m_coreEngine->interfaces().debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = CdbCore::msgComFailed("ReadVirtual", hr);
|
||||
return CallFailed;
|
||||
}
|
||||
// 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 CallFailed;
|
||||
case 'f':
|
||||
*errorMessage = QString::fromLatin1("Dumper call '%1' failed.").arg(callCmd);
|
||||
return CallFailed;
|
||||
default:
|
||||
*errorMessage = QString::fromLatin1("Dumper call '%1' failed ('%2').").arg(callCmd).arg(QLatin1Char(result));
|
||||
return CallFailed;
|
||||
}
|
||||
*outDataPtr = m_buffer + 1;
|
||||
return CallOk;
|
||||
}
|
||||
|
||||
static inline QString msgDumpFailed(const WatchData &wd, const QString *why)
|
||||
{
|
||||
return QString::fromLatin1("Unable to dump '%1' (%2): %3").arg(QString::fromLatin1(wd.iname), wd.type, *why);
|
||||
}
|
||||
|
||||
static inline QString msgNotHandled(const QString &type)
|
||||
{
|
||||
return QString::fromLatin1("The type '%1' is not handled.").arg(type);
|
||||
}
|
||||
|
||||
CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool dumpChildren,
|
||||
QList<WatchData> *result, QString *errorMessage)
|
||||
{
|
||||
if (dumpDebug || debugCDBExecution)
|
||||
qDebug() << ">dumpType() thread: " << m_dumperCallThread << " state: " << m_state
|
||||
<< wd.iname << wd.type << QTime::currentTime().toString();
|
||||
const CdbDumperHelper::DumpResult rc = dumpTypeI(wd, dumpChildren, result, errorMessage);
|
||||
if (dumpDebug)
|
||||
qDebug() << "<dumpType() state: " << m_state << wd.iname
|
||||
<< wd.type << " returns " << rc << *errorMessage << QTime::currentTime().toString();
|
||||
return rc;
|
||||
}
|
||||
|
||||
CdbDumperHelper::DumpResult CdbDumperHelper::dumpTypeI(const WatchData &wd, bool dumpChildren,
|
||||
QList<WatchData> *result, QString *errorMessage)
|
||||
{
|
||||
errorMessage->clear();
|
||||
// Check failure cache and supported types
|
||||
if (m_state == Disabled) {
|
||||
*errorMessage =m_msgDisabled;
|
||||
return DumpNotHandled;
|
||||
}
|
||||
if (wd.error) {
|
||||
*errorMessage =m_msgNotInScope;
|
||||
return DumpNotHandled;
|
||||
}
|
||||
if (m_failedTypes.contains(wd.type)) {
|
||||
*errorMessage = msgNotHandled(wd.type);
|
||||
return DumpNotHandled;
|
||||
}
|
||||
if (wd.address == 0) {
|
||||
*errorMessage = QString::fromLatin1("Address is missing for '%1' (%2).")
|
||||
.arg(QString::fromUtf8(wd.exp)).arg(QString::fromUtf8(wd.type));
|
||||
return DumpNotHandled;
|
||||
}
|
||||
|
||||
// Do we have a thread
|
||||
if (m_dumperCallThread == InvalidDumperCallThread) {
|
||||
*errorMessage = QString::fromLatin1("No thread to call.");
|
||||
if (loadDebug)
|
||||
qDebug() << *errorMessage;
|
||||
return DumpNotHandled;
|
||||
}
|
||||
|
||||
// Delay initialization as much as possible
|
||||
if (isIntOrFloatType(wd.type)) {
|
||||
*errorMessage = QString::fromLatin1("Unhandled POD: " ) + wd.type;
|
||||
return DumpNotHandled;
|
||||
}
|
||||
|
||||
// Ensure types are parsed and known.
|
||||
if (!CdbDumperInitThread::ensureDumperInitialized(*this, errorMessage)) {
|
||||
*errorMessage = msgDumpFailed(wd, errorMessage);
|
||||
m_engine->showMessage(*errorMessage, LogError);
|
||||
return DumpError;
|
||||
}
|
||||
|
||||
// Known type?
|
||||
const QtDumperHelper::TypeData td = m_helper.typeData(wd.type);
|
||||
if (loadDebug)
|
||||
qDebug() << "dumpType" << wd.type << td;
|
||||
if (td.type == QtDumperHelper::UnknownType) {
|
||||
*errorMessage = msgNotHandled(wd.type);
|
||||
return DumpNotHandled;
|
||||
}
|
||||
|
||||
// Now evaluate
|
||||
const QString message = QCoreApplication::translate("Debugger::Internal::CdbDumperHelper",
|
||||
"Querying dumpers for '%1'/'%2' (%3)").
|
||||
arg(QString::fromLatin1(wd.iname), wd.exp, wd.type);
|
||||
m_engine->showMessage(message);
|
||||
|
||||
const DumpExecuteResult der = executeDump(wd, td, dumpChildren, result, errorMessage);
|
||||
if (der == DumpExecuteOk)
|
||||
return DumpOk;
|
||||
if (der == CallSyntaxError) {
|
||||
m_failedTypes.push_back(wd.type);
|
||||
if (dumpDebug)
|
||||
qDebug() << "Caching failing type/expression evaluation failed for " << wd.type;
|
||||
}
|
||||
// log error
|
||||
*errorMessage = msgDumpFailed(wd, errorMessage);
|
||||
m_engine->showMessage(*errorMessage, LogWarning);
|
||||
return DumpError;
|
||||
}
|
||||
|
||||
CdbDumperHelper::DumpExecuteResult
|
||||
CdbDumperHelper::executeDump(const WatchData &wd,
|
||||
const QtDumperHelper::TypeData& td, bool dumpChildren,
|
||||
QList<WatchData> *result, QString *errorMessage)
|
||||
{
|
||||
QByteArray inBuffer;
|
||||
QList<QByteArray> extraParameters;
|
||||
// Build parameter list.
|
||||
m_helper.evaluationParameters(wd, td, QtDumperHelper::CdbDebugger, &inBuffer, &extraParameters);
|
||||
QString callCmd;
|
||||
QTextStream str(&callCmd);
|
||||
str << ".call " << m_dumpObjectSymbol << "(2,0," << wd.hexAddress() << ',' << (dumpChildren ? 1 : 0);
|
||||
foreach(const QByteArray &e, extraParameters)
|
||||
str << ',' << QString::fromUtf8(e);
|
||||
str << ')';
|
||||
if (dumpDebug)
|
||||
qDebug() << "Query: " << wd.toString() << "\nwith: " << callCmd << '\n';
|
||||
const char *outputData;
|
||||
// Completely ignore EXCEPTION_ACCESS_VIOLATION crashes in the dumpers.
|
||||
ExceptionBlocker eb(m_coreEngine->interfaces().debugControl, EXCEPTION_ACCESS_VIOLATION, ExceptionBlocker::IgnoreException);
|
||||
if (!eb) {
|
||||
*errorMessage = eb.errorString();
|
||||
return DumpExecuteCallFailed;
|
||||
}
|
||||
switch (callDumper(callCmd, inBuffer, &outputData, true, errorMessage)) {
|
||||
case CallFailed:
|
||||
return DumpExecuteCallFailed;
|
||||
case CallSyntaxError:
|
||||
return DumpExpressionFailed;
|
||||
case CallOk:
|
||||
break;
|
||||
}
|
||||
if (!QtDumperHelper::parseValue(outputData, result)) {
|
||||
*errorMessage = QLatin1String("Parsing of value query output failed.");
|
||||
return DumpExecuteCallFailed;
|
||||
}
|
||||
return DumpExecuteOk;
|
||||
}
|
||||
|
||||
// Simplify some types for sizeof expressions
|
||||
static void simplifySizeExpression(QByteArray *typeName)
|
||||
{
|
||||
typeName->replace("std::basic_string<char,std::char_traits<char>,std::allocator<char>>",
|
||||
"std::string");
|
||||
}
|
||||
|
||||
bool CdbDumperHelper::getTypeSize(const QByteArray &typeNameIn, int *size, QString *errorMessage)
|
||||
{
|
||||
if (loadDebug > 1)
|
||||
qDebug() << Q_FUNC_INFO << typeNameIn;
|
||||
// Look up cache
|
||||
QByteArray typeName = typeNameIn;
|
||||
simplifySizeExpression(&typeName);
|
||||
// "std::" types sometimes only work without namespace.
|
||||
// If it fails, try again with stripped namespace
|
||||
*size = 0;
|
||||
bool success = false;
|
||||
do {
|
||||
if (runTypeSizeQuery(typeName, size, errorMessage)) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
if (!typeName.contains("std::"))
|
||||
break;
|
||||
typeName.replace("std::", "");
|
||||
errorMessage->clear();
|
||||
if (!runTypeSizeQuery(typeName, size, errorMessage))
|
||||
break;
|
||||
success = true;
|
||||
} while (false);
|
||||
// Cache in dumper helper
|
||||
if (success)
|
||||
m_helper.addSize(typeName, *size);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool CdbDumperHelper::runTypeSizeQuery
|
||||
(const QByteArray &typeName, int *size, QString *errorMessage)
|
||||
{
|
||||
// Retrieve by C++ expression. If we knew the module, we could make use
|
||||
// of the TypeId query mechanism provided by the IDebugSymbolGroup.
|
||||
DEBUG_VALUE sizeValue;
|
||||
QByteArray expression = "sizeof(" + typeName + ')';
|
||||
if (!m_coreEngine->evaluateExpression(expression, &sizeValue, errorMessage))
|
||||
return false;
|
||||
qint64 size64;
|
||||
if (!CdbCore::debugValueToInteger(sizeValue, &size64)) {
|
||||
*errorMessage = QLatin1String("Expression result is not an integer");
|
||||
return false;
|
||||
}
|
||||
*size = static_cast<int>(size64);
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned long CdbDumperHelper::dumperCallThread()
|
||||
{
|
||||
return m_dumperCallThread;
|
||||
}
|
||||
|
||||
void CdbDumperHelper::setDumperCallThread(unsigned long t)
|
||||
{
|
||||
if (m_dumperCallThread != t) {
|
||||
m_dumperCallThread = t;
|
||||
m_goCommand = goCommand(m_dumperCallThread);
|
||||
}
|
||||
}
|
||||
|
||||
const CdbCore::ComInterfaces *CdbDumperHelper::comInterfaces() const
|
||||
{
|
||||
return &m_coreEngine->interfaces();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#include "cdbdumperhelper.moc"
|
||||
@@ -1,171 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBDUMPERHELPER_H
|
||||
#define CDBDUMPERHELPER_H
|
||||
|
||||
#include "watchutils.h"
|
||||
#include "cdbcom.h"
|
||||
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QMap>
|
||||
|
||||
namespace CdbCore {
|
||||
class CoreEngine;
|
||||
struct ComInterfaces;
|
||||
}
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class CdbDumperInitThread;
|
||||
class CdbEngine;
|
||||
|
||||
/* For code clarity, all the stuff related to custom dumpers goes here.
|
||||
* "Custom dumper" is a library compiled against the current
|
||||
* Qt containing functions to evaluate values of Qt classes
|
||||
* (such as QString), taking pointers to their addresses.
|
||||
* The dumper functions produce formatted string output which is
|
||||
* converted into WatchData items with the help of QtDumperHelper.
|
||||
*
|
||||
* Usage: When launching the debugger, call reset() with path to dumpers
|
||||
* and enabled flag. From the module load event callback, call
|
||||
* moduleLoadHook() to initialize.
|
||||
* dumpType() is the main query function to obtain a list of WatchData from
|
||||
* WatchData item produced by the smbol context.
|
||||
* Call disable(), should the debuggee crash (as performing debuggee
|
||||
* calls is no longer possible, then).
|
||||
*
|
||||
* dumperCallThread specifies the thread to use when making the calls.
|
||||
* As of Debugging Tools v 6.11.1.404 (6.10.2009), calls cannot be executed
|
||||
* when the current thread is in some WaitFor...() function. The call will
|
||||
* then hang (regardless whether that thread or some other, non-blocking thread
|
||||
* is used), and the debuggee will be in running state afterwards (causing errors
|
||||
* from ReadVirtual, etc).
|
||||
* The current thread can be used when stepping or a breakpoint was
|
||||
* hit. When interrupting the inferior, an artifical thread is created,
|
||||
* that is not usable, either. */
|
||||
|
||||
class CdbDumperHelper
|
||||
{
|
||||
Q_DISABLE_COPY(CdbDumperHelper)
|
||||
public:
|
||||
enum State {
|
||||
Disabled, // Disabled or failed
|
||||
NotLoaded,
|
||||
InjectLoadFailed,
|
||||
InjectLoading,
|
||||
Loaded,
|
||||
Initialized, // List of types, etc. retrieved
|
||||
};
|
||||
|
||||
explicit CdbDumperHelper(CdbEngine *engine,
|
||||
CdbCore::CoreEngine *coreEngine);
|
||||
~CdbDumperHelper();
|
||||
|
||||
State state() const { return m_state; }
|
||||
bool isEnabled() const { return m_state != Disabled; }
|
||||
|
||||
void setFastSymbolResolution(bool b) { m_fastSymbolResolution = b; }
|
||||
|
||||
// Disable in case of a debuggee crash.
|
||||
void disable();
|
||||
|
||||
// Call before starting the debugger
|
||||
void reset(const QString &library, bool enabled);
|
||||
|
||||
// Call from the module load callback to perform initialization.
|
||||
void moduleLoadHook(const QString &module, HANDLE debuggeeHandle);
|
||||
|
||||
// Dump a WatchData item.
|
||||
enum DumpResult { DumpNotHandled, DumpOk, DumpError };
|
||||
DumpResult dumpType(const WatchData &d, bool dumpChildren,
|
||||
QList<WatchData> *result, QString *errorMessage);
|
||||
|
||||
const CdbCore::ComInterfaces *comInterfaces() const;
|
||||
|
||||
enum { InvalidDumperCallThread = 0xFFFFFFFF };
|
||||
unsigned long dumperCallThread();
|
||||
void setDumperCallThread(unsigned long t);
|
||||
|
||||
private:
|
||||
friend class CdbDumperInitThread;
|
||||
enum CallLoadResult { CallLoadOk, CallLoadError, CallLoadNoQtApp, CallLoadAlreadyLoaded };
|
||||
|
||||
void clearBuffer();
|
||||
CallLoadResult initCallLoad(QString *errorMessage);
|
||||
bool initResolveSymbols(QString *errorMessage);
|
||||
bool initKnownTypes(QString *errorMessage);
|
||||
|
||||
inline DumpResult dumpTypeI(const WatchData &d, bool dumpChildren,
|
||||
QList<WatchData> *result, QString *errorMessage);
|
||||
|
||||
bool getTypeSize(const QByteArray &typeName, int *size, QString *errorMessage);
|
||||
bool runTypeSizeQuery(const QByteArray &typeName, int *size, QString *errorMessage);
|
||||
enum CallResult { CallOk, CallSyntaxError, CallFailed };
|
||||
CallResult callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr,
|
||||
bool ignoreAccessViolation, QString *errorMessage);
|
||||
|
||||
enum DumpExecuteResult { DumpExecuteOk, DumpExpressionFailed, DumpExecuteCallFailed };
|
||||
DumpExecuteResult executeDump(const WatchData &wd,
|
||||
const QtDumperHelper::TypeData& td, bool dumpChildren,
|
||||
QList<WatchData> *result, QString *errorMessage);
|
||||
|
||||
const bool m_tryInjectLoad;
|
||||
const QString m_msgDisabled;
|
||||
const QString m_msgNotInScope;
|
||||
State m_state;
|
||||
CdbEngine *m_engine;
|
||||
CdbCore::CoreEngine *m_coreEngine;
|
||||
|
||||
QString m_library;
|
||||
QString m_dumpObjectSymbol;
|
||||
|
||||
quint64 m_inBufferAddress;
|
||||
unsigned long m_inBufferSize;
|
||||
quint64 m_outBufferAddress;
|
||||
unsigned long m_outBufferSize;
|
||||
char *m_buffer;
|
||||
|
||||
QStringList m_failedTypes;
|
||||
|
||||
QtDumperHelper m_helper;
|
||||
unsigned long m_dumperCallThread;
|
||||
QString m_goCommand;
|
||||
bool m_fastSymbolResolution;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // CDBDUMPERHELPER_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,135 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef DEBUGGER_CDBENGINE_H
|
||||
#define DEBUGGER_CDBENGINE_H
|
||||
|
||||
#include "debuggerengine.h"
|
||||
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class DisassemblerAgent;
|
||||
class CdbDebugEventCallback;
|
||||
class CdbDebugOutput;
|
||||
class CdbEnginePrivate;
|
||||
struct CdbOptions;
|
||||
|
||||
class CdbEngine : public DebuggerEngine
|
||||
{
|
||||
Q_OBJECT
|
||||
explicit CdbEngine(const DebuggerStartParameters &sp);
|
||||
|
||||
public:
|
||||
~CdbEngine();
|
||||
|
||||
// Factory function that returns 0 if the debug engine library cannot be found.
|
||||
static DebuggerEngine *create(const DebuggerStartParameters &sp,
|
||||
QString *errorMessage);
|
||||
|
||||
virtual void setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos);
|
||||
virtual void setupEngine();
|
||||
virtual void setupInferior();
|
||||
virtual void runEngine();
|
||||
virtual void shutdownInferior();
|
||||
virtual void shutdownEngine();
|
||||
virtual void detachDebugger();
|
||||
virtual void updateWatchData(const WatchData &data, const WatchUpdateFlags &flags);
|
||||
virtual unsigned debuggerCapabilities() const;
|
||||
|
||||
virtual void executeStep();
|
||||
virtual void executeStepOut();
|
||||
virtual void executeNext();
|
||||
virtual void executeStepI();
|
||||
virtual void executeNextI();
|
||||
|
||||
virtual void continueInferior();
|
||||
virtual void interruptInferior();
|
||||
|
||||
virtual void executeRunToLine(const QString &fileName, int lineNumber);
|
||||
virtual void executeRunToFunction(const QString &functionName);
|
||||
virtual void executeJumpToLine(const QString &fileName, int lineNumber);
|
||||
virtual void assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value);
|
||||
virtual void executeDebuggerCommand(const QString &command);
|
||||
|
||||
virtual void activateFrame(int index);
|
||||
virtual void selectThread(int index);
|
||||
|
||||
virtual bool stateAcceptsBreakpointChanges() const;
|
||||
virtual bool acceptsBreakpoint(BreakpointId id) const;
|
||||
virtual void attemptBreakpointSynchronization();
|
||||
|
||||
virtual void setRegisterValue(int regnr, const QString &value);
|
||||
virtual void fetchDisassembler(DisassemblerAgent *agent);
|
||||
virtual void fetchMemory(MemoryAgent *, QObject *, quint64 addr, quint64 length);
|
||||
|
||||
virtual void reloadModules();
|
||||
virtual void loadSymbols(const QString &moduleName);
|
||||
virtual void loadAllSymbols();
|
||||
virtual void requestModuleSymbols(const QString &moduleName);
|
||||
|
||||
virtual void reloadRegisters();
|
||||
virtual void reloadSourceFiles();
|
||||
virtual void reloadFullStack() {}
|
||||
|
||||
public slots:
|
||||
void syncDebuggerPaths();
|
||||
|
||||
private slots:
|
||||
void slotConsoleStubStarted();
|
||||
void slotConsoleStubMessage(const QString &msg, bool);
|
||||
void slotConsoleStubTerminated();
|
||||
void slotBreakAttachToCrashed();
|
||||
void warning(const QString &w);
|
||||
|
||||
private:
|
||||
inline bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage);
|
||||
void processTerminated(unsigned long exitCode);
|
||||
void evaluateWatcher(WatchData *wd);
|
||||
QString editorToolTip(const QString &exp, const QString &function);
|
||||
bool step(unsigned long executionStatus);
|
||||
bool attemptBreakpointSynchronizationI(QString *errorMessage);
|
||||
|
||||
CdbEnginePrivate *m_d;
|
||||
|
||||
friend class CdbEnginePrivate;
|
||||
friend class CdbDebugEventCallback;
|
||||
friend class CdbDebugOutput;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // DEBUGGER_CDBENGINE_H
|
||||
@@ -1,156 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef DEBUGGER_CDBENGINEPRIVATE_H
|
||||
#define DEBUGGER_CDBENGINEPRIVATE_H
|
||||
|
||||
#include "coreengine.h"
|
||||
#include "debuggerconstants.h"
|
||||
#include "cdboptions.h"
|
||||
#include "cdbdumperhelper.h"
|
||||
#include "stackhandler.h"
|
||||
|
||||
#include <utils/consoleprocess.h>
|
||||
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QMap>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class WatchHandler;
|
||||
class CdbStackTraceContext;
|
||||
class CdbSymbolGroupContext;
|
||||
|
||||
class CdbEnginePrivate : public CdbCore::CoreEngine
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
typedef QMap<QString, QString> EditorToolTipCache;
|
||||
|
||||
enum HandleBreakEventMode { // Special modes for break event handler.
|
||||
BreakEventHandle,
|
||||
BreakEventIgnoreOnce,
|
||||
BreakEventSyncBreakPoints,
|
||||
};
|
||||
|
||||
enum StoppedReason {
|
||||
StoppedCrash,
|
||||
StoppedBreakpoint,
|
||||
StoppedOther
|
||||
};
|
||||
|
||||
explicit CdbEnginePrivate(CdbEngine* engine);
|
||||
~CdbEnginePrivate();
|
||||
bool init(QString *errorMessage);
|
||||
|
||||
void checkVersion();
|
||||
void processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle);
|
||||
void setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread);
|
||||
|
||||
bool isDebuggeeRunning() const { return isWatchTimerRunning(); }
|
||||
ULONG updateThreadList(const QString ¤tThreadState = QString());
|
||||
bool setCDBThreadId(unsigned long threadId, QString *errorMessage);
|
||||
void updateStackTrace();
|
||||
void updateModules();
|
||||
|
||||
void handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP);
|
||||
void cleanStackTrace();
|
||||
void clearForRun();
|
||||
void handleModuleLoad(quint64 offset, const QString &);
|
||||
void handleModuleUnload(const QString &name);
|
||||
CdbSymbolGroupContext *getSymbolGroupContext(int frameIndex, QString *errorMessage) const;
|
||||
void clearDisplay();
|
||||
|
||||
bool interruptInterferiorProcess(QString *errorMessage);
|
||||
|
||||
bool continueInferiorProcess(QString *errorMessage = 0);
|
||||
bool continueInferior(QString *errorMessage);
|
||||
bool executeContinueCommand(const QString &command);
|
||||
|
||||
void notifyException(long code, bool fatal, const QString &message);
|
||||
|
||||
|
||||
bool endInferior(bool detachOnly /* = false */, QString *errorMessage);
|
||||
|
||||
void endDebugging(bool detachOnly = false);
|
||||
|
||||
void updateCodeLevel();
|
||||
|
||||
QString stoppedMessage(const StackFrame *topFrame = 0) const;
|
||||
|
||||
private slots:
|
||||
void handleDebugEvent();
|
||||
void slotModulesLoaded();
|
||||
|
||||
public:
|
||||
const QSharedPointer<CdbOptions> m_options;
|
||||
HANDLE m_hDebuggeeProcess;
|
||||
HANDLE m_hDebuggeeThread;
|
||||
bool m_interrupted;
|
||||
int m_currentThreadId;
|
||||
int m_eventThreadId;
|
||||
int m_interruptArticifialThreadId;
|
||||
bool m_ignoreInitialBreakPoint;
|
||||
HandleBreakEventMode m_breakEventMode;
|
||||
|
||||
QSharedPointer<CdbDumperHelper> m_dumper;
|
||||
|
||||
CdbEngine *m_engine;
|
||||
CdbStackTraceContext *m_currentStackTrace;
|
||||
EditorToolTipCache m_editorToolTipCache;
|
||||
|
||||
bool m_firstActivatedFrame;
|
||||
bool m_inferiorStartupComplete;
|
||||
|
||||
DebuggerStartMode m_mode;
|
||||
Utils::ConsoleProcess m_consoleStubProc;
|
||||
|
||||
StoppedReason m_stoppedReason;
|
||||
QString m_stoppedMessage;
|
||||
};
|
||||
|
||||
enum { messageTimeOut = 5000 };
|
||||
|
||||
enum { debugCDB = 0 };
|
||||
enum { debugCDBExecution = 0 };
|
||||
enum { debugCDBWatchHandling = 0 };
|
||||
enum { debugToolTips = 0 };
|
||||
enum { debugBreakpoints = 0 };
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // DEBUGGER_CDBENGINEPRIVATE_H
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbexceptionutils.h"
|
||||
#include "cdbengine_p.h"
|
||||
#include "stacktracecontext.h"
|
||||
#include "dbgwinutils.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
enum { debugExc = 0 };
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
static inline void formatDebugFilterExecFlags(ULONG f, const char *title, QTextStream &str)
|
||||
{
|
||||
str.setIntegerBase(16);
|
||||
str << ' ' << title << "=0x" << f << " (";
|
||||
str.setIntegerBase(10);
|
||||
switch (f) {
|
||||
case DEBUG_FILTER_BREAK:
|
||||
str << "DEBUG_FILTER_BREAK";
|
||||
break;
|
||||
case DEBUG_FILTER_SECOND_CHANCE_BREAK:
|
||||
str << "DEBUG_FILTER_SECOND_CHANCE_BREAK";
|
||||
break;
|
||||
case DEBUG_FILTER_OUTPUT:
|
||||
str << "DEBUG_FILTER_OUTPUT";
|
||||
break;
|
||||
case DEBUG_FILTER_IGNORE:
|
||||
str << "DEBUG_FILTER_IGNORE";
|
||||
break;
|
||||
}
|
||||
str << ')';
|
||||
}
|
||||
|
||||
static inline void formatDebugFilterContFlags(ULONG f, const char *title, QTextStream &str)
|
||||
{
|
||||
str.setIntegerBase(16);
|
||||
str << ' ' << title << "=0x" << f << " (";
|
||||
str.setIntegerBase(10);
|
||||
switch (f) {
|
||||
case DEBUG_FILTER_GO_HANDLED:
|
||||
str << "DEBUG_FILTER_GO_HANDLED";
|
||||
break;
|
||||
case DEBUG_FILTER_GO_NOT_HANDLED:
|
||||
str << "DEBUG_FILTER_GO_NOT_HANDLED";
|
||||
}
|
||||
str << ')';
|
||||
}
|
||||
|
||||
ExceptionBlocker::ExceptionBlocker(CIDebugControl *ctrl, ULONG code, Mode m) :
|
||||
m_ctrl(ctrl),
|
||||
m_code(code),
|
||||
m_state(StateError)
|
||||
{
|
||||
// Retrieve current state
|
||||
memset(&m_oldParameters, 0, sizeof(DEBUG_EXCEPTION_FILTER_PARAMETERS));
|
||||
if (getExceptionParameters(ctrl, code, &m_oldParameters, &m_errorString)) {
|
||||
// Are we in a nested instantiation?
|
||||
const ULONG desiredExOption = m == IgnoreException ? DEBUG_FILTER_IGNORE : DEBUG_FILTER_OUTPUT;
|
||||
const bool isAlreadyBlocked = m_oldParameters.ExecutionOption == desiredExOption
|
||||
&& m_oldParameters.ContinueOption == DEBUG_FILTER_GO_NOT_HANDLED;
|
||||
if (isAlreadyBlocked) {
|
||||
m_state = StateNested;
|
||||
} else {
|
||||
// Nope, block it now.
|
||||
DEBUG_EXCEPTION_FILTER_PARAMETERS blockedState = m_oldParameters;
|
||||
blockedState.ExecutionOption = desiredExOption;
|
||||
blockedState.CommandSize = DEBUG_FILTER_GO_NOT_HANDLED;
|
||||
const bool ok = setExceptionParameters(m_ctrl, blockedState, &m_errorString);
|
||||
m_state = ok ? StateOk : StateError;
|
||||
} // not blocked
|
||||
} else {
|
||||
m_state = StateError;
|
||||
}
|
||||
if (debugExc)
|
||||
qDebug() << "ExceptionBlocker: state=" << m_state << format(m_oldParameters) << m_errorString;
|
||||
}
|
||||
|
||||
ExceptionBlocker::~ExceptionBlocker()
|
||||
{
|
||||
if (m_state == StateOk) {
|
||||
// Restore
|
||||
if (debugExc)
|
||||
qDebug() << "~ExceptionBlocker: unblocking " << m_oldParameters.ExceptionCode;
|
||||
if (!setExceptionParameters(m_ctrl, m_oldParameters, &m_errorString))
|
||||
qWarning("Unable to restore exception state for %lu: %s\n", m_oldParameters.ExceptionCode, qPrintable(m_errorString));
|
||||
}
|
||||
}
|
||||
|
||||
bool ExceptionBlocker::getExceptionParameters(CIDebugControl *ctrl, ULONG exCode, DEBUG_EXCEPTION_FILTER_PARAMETERS *result, QString *errorMessage)
|
||||
{
|
||||
const HRESULT ihr = ctrl->GetExceptionFilterParameters(1, &exCode, 0, result);
|
||||
if (FAILED(ihr)) {
|
||||
*errorMessage = CdbCore::msgComFailed("GetExceptionFilterParameters", ihr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExceptionBlocker::setExceptionParameters(CIDebugControl *ctrl, const DEBUG_EXCEPTION_FILTER_PARAMETERS &p, QString *errorMessage)
|
||||
{
|
||||
const HRESULT ihr = ctrl->SetExceptionFilterParameters(1, const_cast<DEBUG_EXCEPTION_FILTER_PARAMETERS*>(&p));
|
||||
if (FAILED(ihr)) {
|
||||
*errorMessage = CdbCore::msgComFailed("GetExceptionFilterParameters", ihr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString ExceptionBlocker::format(const DEBUG_EXCEPTION_FILTER_PARAMETERS &p)
|
||||
{
|
||||
QString rc;
|
||||
QTextStream str(&rc);
|
||||
str << "Code=" << p.ExceptionCode;
|
||||
formatDebugFilterExecFlags(p.ExecutionOption, "ExecutionOption", str);
|
||||
formatDebugFilterContFlags(p.ContinueOption, "ContinueOption", str);
|
||||
str << " TextSize=" << p.TextSize << " CommandSizes=" << p.CommandSize << ','
|
||||
<< p.SecondCommandSize;
|
||||
return rc;
|
||||
}
|
||||
|
||||
// ------------------ further exception utilities
|
||||
// Simple exception formatting
|
||||
|
||||
void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
|
||||
{
|
||||
formatWindowsException(e->ExceptionCode, e->ExceptionAddress,
|
||||
e->ExceptionFlags,
|
||||
e->ExceptionInformation[0],
|
||||
e->ExceptionInformation[1],
|
||||
str);
|
||||
str << "\n\n";
|
||||
}
|
||||
|
||||
// Format exception with stacktrace in case of C++ exception
|
||||
void formatException(const EXCEPTION_RECORD64 *e,
|
||||
const CdbCore::ComInterfaces *cif,
|
||||
QTextStream &str)
|
||||
{
|
||||
formatException(e, str);
|
||||
if (e->ExceptionCode == winExceptionCppException) {
|
||||
QString errorMessage;
|
||||
if (CdbCore::StackTraceContext *stc = CdbCore::StackTraceContext::create(cif, 9999, &errorMessage)) {
|
||||
str << "at:\n";
|
||||
stc->format(str);
|
||||
str <<'\n';
|
||||
delete stc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -1,100 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBEXCEPTIONUTILS_H
|
||||
#define CDBEXCEPTIONUTILS_H
|
||||
|
||||
#include "cdbcom.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextStream;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace CdbCore {
|
||||
struct ComInterfaces;
|
||||
}
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class CdbDumperHelper;
|
||||
|
||||
// Utility class that blocks out exception handling (breaking)
|
||||
// for a specific exception (like EXCEPTION_ACCESS_VIOLATION) while in scope.
|
||||
class ExceptionBlocker {
|
||||
Q_DISABLE_COPY(ExceptionBlocker)
|
||||
public:
|
||||
// Log mode. Note: Does not influence the output callbacks.
|
||||
enum Mode {
|
||||
IgnoreException, // Ignore & suppress debugger console notification
|
||||
LogException // Ignore, still print console notification
|
||||
};
|
||||
|
||||
ExceptionBlocker(CIDebugControl *ctrl, ULONG exceptionCode, Mode mode);
|
||||
~ExceptionBlocker();
|
||||
|
||||
operator bool() const { return m_state != StateError; }
|
||||
QString errorString() const { return m_errorString; }
|
||||
|
||||
// Helpers
|
||||
static bool getExceptionParameters(CIDebugControl *ctrl, ULONG exCode, DEBUG_EXCEPTION_FILTER_PARAMETERS *result, QString *errorMessage);
|
||||
static bool setExceptionParameters(CIDebugControl *ctrl, const DEBUG_EXCEPTION_FILTER_PARAMETERS &p, QString *errorMessage);
|
||||
static QString format(const DEBUG_EXCEPTION_FILTER_PARAMETERS &p);
|
||||
|
||||
private:
|
||||
enum State { StateOk,
|
||||
StateNested, // Nested call, exception already blocked, do nothing
|
||||
StateError };
|
||||
|
||||
CIDebugControl *m_ctrl;
|
||||
const LONG m_code;
|
||||
DEBUG_EXCEPTION_FILTER_PARAMETERS m_oldParameters;
|
||||
State m_state;
|
||||
QString m_errorString;
|
||||
};
|
||||
|
||||
// Format exception
|
||||
void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str);
|
||||
|
||||
// Format exception with stacktrace in case of C++ exception
|
||||
void formatException(const EXCEPTION_RECORD64 *e,
|
||||
const CdbCore::ComInterfaces *cif,
|
||||
QTextStream &str);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // CDBEXCEPTIONUTILS_H
|
||||
@@ -1,288 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbmodules.h"
|
||||
#include "moduleshandler.h"
|
||||
#include "cdbengine_p.h"
|
||||
#include "breakpoint.h"
|
||||
#include "shared/dbgwinutils.h"
|
||||
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QRegExp>
|
||||
#include <QtCore/QVector>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
static inline bool getModuleCount(CIDebugSymbols *syms, ULONG *count, QString *errorMessage)
|
||||
{
|
||||
*count = 0;
|
||||
ULONG loadedCount, unloadedCount;
|
||||
const HRESULT hr = syms->GetNumberModules(&loadedCount, &unloadedCount);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= CdbCore::msgComFailed("GetNumberModules", hr);
|
||||
return false;
|
||||
}
|
||||
*count = loadedCount + unloadedCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getModuleNameList(CIDebugSymbols *syms, QStringList *modules, QString *errorMessage)
|
||||
{
|
||||
ULONG count;
|
||||
modules->clear();
|
||||
if (!getModuleCount(syms, &count, errorMessage))
|
||||
return false;
|
||||
WCHAR wszBuf[MAX_PATH];
|
||||
for (ULONG m = 0; m < count; m++)
|
||||
if (SUCCEEDED(syms->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, m, 0, wszBuf, MAX_PATH - 1, 0)))
|
||||
modules->push_back(QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get basic module parameters from struct (except name)
|
||||
static inline void getBasicModuleParameters(const DEBUG_MODULE_PARAMETERS &p,
|
||||
Module *module)
|
||||
{
|
||||
if ((p.Flags & DEBUG_MODULE_USER_MODE) && (p.SymbolType != DEBUG_SYMTYPE_NONE))
|
||||
module->symbolsRead = Module::ReadOk;
|
||||
else
|
||||
module->symbolsRead = Module::ReadFailed;
|
||||
module->startAddress = p.Base;
|
||||
module->endAddress = p.Base + p.Size;
|
||||
}
|
||||
|
||||
// Get module name by index
|
||||
static inline bool getModuleName(CIDebugSymbols *syms, ULONG index, QString *n, QString *errorMessage)
|
||||
{
|
||||
WCHAR wszBuf[MAX_PATH];
|
||||
const HRESULT hr = syms->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, index, 0, wszBuf, MAX_PATH - 1, 0);
|
||||
if (FAILED(hr) && hr != E_INVALIDARG) {
|
||||
*errorMessage= CdbCore::msgComFailed("GetModuleNameStringWide", hr);
|
||||
return false;
|
||||
}
|
||||
*n = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getModuleByOffset(CIDebugSymbols *syms, quint64 offset,
|
||||
Module *module, QString *errorMessage)
|
||||
{
|
||||
// Find by base address and set parameters
|
||||
ULONG index;
|
||||
HRESULT hr = syms->GetModuleByOffset(offset, 0, &index, 0);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= CdbCore::msgComFailed("GetModuleByOffset", hr);
|
||||
return false;
|
||||
}
|
||||
DEBUG_MODULE_PARAMETERS parameters;
|
||||
memset(¶meters, 0, sizeof(DEBUG_MODULE_PARAMETERS));
|
||||
hr = syms->GetModuleParameters(1, 0, 0u, ¶meters);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= CdbCore::msgComFailed("GetModuleParameters", hr);
|
||||
return false;
|
||||
}
|
||||
getBasicModuleParameters(parameters, module);
|
||||
return getModuleName(syms, index, &(module->moduleName), errorMessage);
|
||||
}
|
||||
|
||||
bool getModuleList(CIDebugSymbols *syms, Modules *modules, QString *errorMessage)
|
||||
{
|
||||
ULONG count;
|
||||
modules->clear();
|
||||
if (!getModuleCount(syms, &count, errorMessage))
|
||||
return false;
|
||||
QVector<DEBUG_MODULE_PARAMETERS> parameters(count);
|
||||
DEBUG_MODULE_PARAMETERS *parmPtr = &(*parameters.begin());
|
||||
memset(parmPtr, 0, sizeof(DEBUG_MODULE_PARAMETERS) * count);
|
||||
HRESULT hr = syms->GetModuleParameters(count, 0, 0u, parmPtr);
|
||||
// E_INVALIDARG indicates 'Partial results' according to docu
|
||||
if (FAILED(hr) && hr != E_INVALIDARG) {
|
||||
*errorMessage= CdbCore::msgComFailed("GetModuleParameters", hr);
|
||||
return false;
|
||||
}
|
||||
// fill array
|
||||
for (ULONG m = 0; m < count; m++) {
|
||||
const DEBUG_MODULE_PARAMETERS &p = parameters.at(m);
|
||||
if (p.Base != DEBUG_INVALID_OFFSET) { // Partial results?
|
||||
Module module;
|
||||
getBasicModuleParameters(p, &module);
|
||||
if (!getModuleName(syms, m, &(module.moduleName), errorMessage))
|
||||
return false;
|
||||
modules->push_back(module);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Search symbols matching a pattern
|
||||
bool searchSymbols(CIDebugSymbols *syms, const QString &pattern,
|
||||
QStringList *matches, QString *errorMessage)
|
||||
{
|
||||
matches->clear();
|
||||
ULONG64 handle = 0;
|
||||
// E_NOINTERFACE means "no match". Apparently, it does not always
|
||||
// set handle.
|
||||
HRESULT hr = syms->StartSymbolMatchWide(reinterpret_cast<PCWSTR>(pattern.utf16()), &handle);
|
||||
if (hr == E_NOINTERFACE) {
|
||||
if (handle)
|
||||
syms->EndSymbolMatch(handle);
|
||||
return true;
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= CdbCore::msgComFailed("StartSymbolMatchWide", hr);
|
||||
return false;
|
||||
}
|
||||
WCHAR wszBuf[MAX_PATH];
|
||||
while (true) {
|
||||
hr = syms->GetNextSymbolMatchWide(handle, wszBuf, MAX_PATH - 1, 0, 0);
|
||||
if (hr == E_NOINTERFACE)
|
||||
break;
|
||||
if (hr == S_OK)
|
||||
matches->push_back(QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)));
|
||||
}
|
||||
syms->EndSymbolMatch(handle);
|
||||
if (matches->empty())
|
||||
*errorMessage = QString::fromLatin1("No symbol matches '%1'.").arg(pattern);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper for the resolveSymbol overloads.
|
||||
static ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol,
|
||||
QStringList *matches,
|
||||
QString *errorMessage)
|
||||
{
|
||||
errorMessage->clear();
|
||||
// Is it an incomplete symbol?
|
||||
if (symbol->contains(QLatin1Char('!')))
|
||||
return ResolveSymbolOk;
|
||||
const bool withinMSVCRunTime = *symbol == QLatin1String(winMSVCThrowFunction)
|
||||
|| *symbol == QLatin1String(winMSVCCatchFunction);
|
||||
if (*symbol == QLatin1String("qMain")) // 'main' is a #define for gdb, but not for VS
|
||||
*symbol = QLatin1String("main");
|
||||
// resolve
|
||||
if (!searchSymbols(syms, *symbol, matches, errorMessage))
|
||||
return ResolveSymbolError;
|
||||
// Exception functions sometimes show up ambiguously as'QtGuid4!CxxThrowException',
|
||||
// 'MSVCR100D!CxxThrowException', QtCored4!CxxThrowException',
|
||||
// 'MSVCP100D!CxxThrowException' and 'msvcrt!CxxThrowException',
|
||||
// 'OLEAUT32!CxxThrowException'...restrict to MSVC-RunTime (any MSVC version).
|
||||
if (withinMSVCRunTime && matches->size() > 1) {
|
||||
for (QStringList::iterator it = matches->begin(); it != matches->end(); )
|
||||
if (it->startsWith(QLatin1String("MSVCR"))) {
|
||||
++it;
|
||||
} else {
|
||||
it = matches->erase(it);
|
||||
}
|
||||
}
|
||||
if (matches->empty()) {
|
||||
*errorMessage = QString::fromLatin1("No match for '%1' found").arg(*symbol);
|
||||
return ResolveSymbolNotFound;
|
||||
}
|
||||
*symbol = matches->front();
|
||||
if (matches->size() > 1) {
|
||||
*errorMessage = QString::fromLatin1("Ambiguous symbol '%1': %2").
|
||||
arg(*symbol, matches->join(QString(QLatin1Char(' '))));
|
||||
return ResolveSymbolAmbiguous;
|
||||
}
|
||||
return ResolveSymbolOk;
|
||||
}
|
||||
|
||||
// Add missing the module specifier: "main" -> "project!main"
|
||||
ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol,
|
||||
QString *errorMessage)
|
||||
{
|
||||
QStringList matches;
|
||||
return resolveSymbol(syms, symbol, &matches, errorMessage);
|
||||
}
|
||||
|
||||
ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, const QString &pattern, QString *symbol, QString *errorMessage)
|
||||
{
|
||||
QStringList matches;
|
||||
const ResolveSymbolResult r1 = resolveSymbol(syms, symbol, &matches, errorMessage);
|
||||
switch (r1) {
|
||||
case ResolveSymbolOk:
|
||||
case ResolveSymbolNotFound:
|
||||
case ResolveSymbolError:
|
||||
return r1;
|
||||
case ResolveSymbolAmbiguous:
|
||||
break;
|
||||
}
|
||||
// Filter out
|
||||
errorMessage->clear();
|
||||
const QRegExp re(pattern);
|
||||
if (!re.isValid()) {
|
||||
*errorMessage = QString::fromLatin1("Internal error: Invalid pattern '%1'.").arg(pattern);
|
||||
return ResolveSymbolError;
|
||||
}
|
||||
const QStringList filteredMatches = matches.filter(re);
|
||||
if (filteredMatches.size() == 1) {
|
||||
*symbol = filteredMatches.front();
|
||||
return ResolveSymbolOk;
|
||||
}
|
||||
// something went wrong
|
||||
const QString matchesString = matches.join(QString(QLatin1Char(',')));
|
||||
if (filteredMatches.empty()) {
|
||||
*errorMessage = QString::fromLatin1("None of symbols '%1' found for '%2' matches '%3'.").
|
||||
arg(matchesString, *symbol, pattern);
|
||||
return ResolveSymbolNotFound;
|
||||
}
|
||||
*errorMessage = QString::fromLatin1("Ambiguous match of symbols '%1' found for '%2' (%3)").
|
||||
arg(matchesString, *symbol, pattern);
|
||||
return ResolveSymbolAmbiguous;
|
||||
}
|
||||
|
||||
// List symbols of a module
|
||||
bool getModuleSymbols(CIDebugSymbols *syms, const QString &moduleName,
|
||||
Symbols *symbols, QString *errorMessage)
|
||||
{
|
||||
// Search all symbols and retrieve addresses
|
||||
symbols->clear();
|
||||
QStringList matches;
|
||||
const QString pattern = QFileInfo(moduleName).baseName() + QLatin1String("!*");
|
||||
if (!searchSymbols(syms, pattern, &matches, errorMessage))
|
||||
return false;
|
||||
const QString hexPrefix = QLatin1String("0x");
|
||||
foreach (const QString &name, matches) {
|
||||
Symbol symbol;
|
||||
symbol.name = name;
|
||||
ULONG64 offset = 0;
|
||||
if (SUCCEEDED(syms->GetOffsetByNameWide(reinterpret_cast<PCWSTR>(name.utf16()), &offset)))
|
||||
symbol.address = hexPrefix + QString::number(offset, 16);
|
||||
symbols->push_back(symbol);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -1,77 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBMODULES_H
|
||||
#define CDBMODULES_H
|
||||
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QVector>
|
||||
|
||||
#include "cdbcom.h"
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class Module;
|
||||
class Symbol;
|
||||
typedef QVector<Module> Modules;
|
||||
typedef QVector<Symbol> Symbols;
|
||||
|
||||
bool getModuleList(CIDebugSymbols *syms, Modules *modules, QString *errorMessage);
|
||||
bool getModuleNameList(CIDebugSymbols *syms, QStringList *modules, QString *errorMessage);
|
||||
bool getModuleByOffset(CIDebugSymbols *syms, quint64 offset, Module *module, QString *errorMessage);
|
||||
|
||||
// Search symbols matching a pattern. Does not filter on module names.
|
||||
bool searchSymbols(CIDebugSymbols *syms, const QString &pattern,
|
||||
QStringList *matches, QString *errorMessage);
|
||||
|
||||
// ResolveSymbol: For symbols that are missing the module specifier,
|
||||
// find the module and expand: "main" -> "project!main".
|
||||
|
||||
enum ResolveSymbolResult { ResolveSymbolOk, ResolveSymbolAmbiguous,
|
||||
ResolveSymbolNotFound, ResolveSymbolError };
|
||||
|
||||
// Resolve a symbol that is unique to all modules
|
||||
ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol, QString *errorMessage);
|
||||
|
||||
// Resolve symbol overload with an additional regexp pattern to filter on modules.
|
||||
ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, const QString &pattern, QString *symbol, QString *errorMessage);
|
||||
|
||||
// List symbols of a module
|
||||
bool getModuleSymbols(CIDebugSymbols *syms, const QString &moduleName,
|
||||
Symbols *symbols, QString *errorMessage);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // CDBMODULES_H
|
||||
@@ -1,136 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdboptions.h"
|
||||
#include "coreengine.h"
|
||||
#include "cdbsymbolpathlisteditor.h"
|
||||
|
||||
#include <QtCore/QSettings>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFileInfo>
|
||||
|
||||
static const char *settingsGroupC = "CDB";
|
||||
static const char *enabledKeyC = "Enabled";
|
||||
static const char *pathKeyC = "Path";
|
||||
static const char *symbolPathsKeyC = "SymbolPaths";
|
||||
static const char *sourcePathsKeyC = "SourcePaths";
|
||||
static const char *breakOnExceptionKeyC = "BreakOnException";
|
||||
static const char *verboseSymbolLoadingKeyC = "VerboseSymbolLoading";
|
||||
static const char *fastLoadDebuggingHelpersKeyC = "FastLoadDebuggingHelpers";
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
CdbOptions::CdbOptions() :
|
||||
enabled(false),
|
||||
breakOnException(false),
|
||||
verboseSymbolLoading(false),
|
||||
fastLoadDebuggingHelpers(true)
|
||||
{
|
||||
}
|
||||
|
||||
QString CdbOptions::settingsGroup()
|
||||
{
|
||||
return QLatin1String(settingsGroupC);
|
||||
}
|
||||
|
||||
void CdbOptions::clear()
|
||||
{
|
||||
enabled = false;
|
||||
verboseSymbolLoading = false;
|
||||
fastLoadDebuggingHelpers = true;
|
||||
path.clear();
|
||||
}
|
||||
|
||||
void CdbOptions::fromSettings(const QSettings *s)
|
||||
{
|
||||
clear();
|
||||
// Is this the first time we are called ->
|
||||
// try to find automatically
|
||||
const QString keyRoot = QLatin1String(settingsGroupC) + QLatin1Char('/');
|
||||
const QString enabledKey = keyRoot + QLatin1String(enabledKeyC);
|
||||
const bool firstTime = !s->contains(enabledKey);
|
||||
if (firstTime) {
|
||||
enabled = CdbCore::CoreEngine::autoDetectPath(&path);
|
||||
return;
|
||||
}
|
||||
enabled = s->value(enabledKey, false).toBool();
|
||||
path = s->value(keyRoot + QLatin1String(pathKeyC), QString()).toString();
|
||||
symbolPaths = s->value(keyRoot + QLatin1String(symbolPathsKeyC), QStringList()).toStringList();
|
||||
sourcePaths = s->value(keyRoot + QLatin1String(sourcePathsKeyC), QStringList()).toStringList();
|
||||
verboseSymbolLoading = s->value(keyRoot + QLatin1String(verboseSymbolLoadingKeyC), false).toBool();
|
||||
fastLoadDebuggingHelpers = s->value(keyRoot + QLatin1String(fastLoadDebuggingHelpersKeyC), true).toBool();
|
||||
breakOnException = s->value(keyRoot + QLatin1String(breakOnExceptionKeyC), false).toBool();
|
||||
}
|
||||
|
||||
void CdbOptions::toSettings(QSettings *s) const
|
||||
{
|
||||
s->beginGroup(QLatin1String(settingsGroupC));
|
||||
s->setValue(QLatin1String(enabledKeyC), enabled);
|
||||
s->setValue(QLatin1String(pathKeyC), path);
|
||||
s->setValue(QLatin1String(symbolPathsKeyC), symbolPaths);
|
||||
s->setValue(QLatin1String(sourcePathsKeyC), sourcePaths);
|
||||
s->setValue(QLatin1String(verboseSymbolLoadingKeyC), verboseSymbolLoading);
|
||||
s->setValue(QLatin1String(fastLoadDebuggingHelpersKeyC), fastLoadDebuggingHelpers);
|
||||
s->setValue(QLatin1String(breakOnExceptionKeyC), breakOnException);
|
||||
s->endGroup();
|
||||
}
|
||||
|
||||
unsigned CdbOptions::compare(const CdbOptions &rhs) const
|
||||
{
|
||||
unsigned rc = 0;
|
||||
if (enabled != rhs.enabled || path != rhs.path)
|
||||
rc |= InitializationOptionsChanged;
|
||||
if (symbolPaths != rhs.symbolPaths || sourcePaths != rhs.sourcePaths)
|
||||
rc |= DebuggerPathsChanged;
|
||||
if (verboseSymbolLoading != rhs.verboseSymbolLoading)
|
||||
rc |= SymbolOptionsChanged;
|
||||
if (fastLoadDebuggingHelpers != rhs.fastLoadDebuggingHelpers)
|
||||
rc |= FastLoadDebuggingHelpersChanged;
|
||||
if (breakOnException != rhs.breakOnException)
|
||||
rc |= OtherOptionsChanged;
|
||||
return rc;
|
||||
}
|
||||
|
||||
QString CdbOptions::symbolServerPath(const QString &cacheDir)
|
||||
{
|
||||
return CdbSymbolPathListEditor::symbolServerPath(cacheDir);
|
||||
}
|
||||
|
||||
int CdbOptions::indexOfSymbolServerPath(const QStringList &symbolPaths, QString *cacheDir)
|
||||
{
|
||||
return CdbSymbolPathListEditor::indexOfSymbolServerPath(symbolPaths, cacheDir);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -1,88 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBSETTINGS_H
|
||||
#define CDBSETTINGS_H
|
||||
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSettings;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
struct CdbOptions
|
||||
{
|
||||
public:
|
||||
CdbOptions();
|
||||
void clear();
|
||||
|
||||
void fromSettings(const QSettings *s);
|
||||
void toSettings(QSettings *s) const;
|
||||
|
||||
// A set of flags for comparison function.
|
||||
enum ChangeFlags { InitializationOptionsChanged = 0x1,
|
||||
DebuggerPathsChanged = 0x2,
|
||||
SymbolOptionsChanged = 0x4,
|
||||
FastLoadDebuggingHelpersChanged = 0x8,
|
||||
OtherOptionsChanged = 0x100
|
||||
};
|
||||
unsigned compare(const CdbOptions &s) const;
|
||||
|
||||
// Format a symbol server specification with a cache directory
|
||||
static QString symbolServerPath(const QString &cacheDir);
|
||||
// Check whether the path is a symbol server specification and return the cache directory
|
||||
static int indexOfSymbolServerPath(const QStringList &symbolPaths, QString *cacheDir = 0);
|
||||
|
||||
static QString settingsGroup();
|
||||
|
||||
bool enabled;
|
||||
QString path;
|
||||
QStringList symbolPaths;
|
||||
QStringList sourcePaths;
|
||||
bool breakOnException;
|
||||
bool verboseSymbolLoading;
|
||||
bool fastLoadDebuggingHelpers;
|
||||
};
|
||||
|
||||
inline bool operator==(const CdbOptions &s1, const CdbOptions &s2)
|
||||
{ return s1.compare(s2) == 0u; }
|
||||
inline bool operator!=(const CdbOptions &s1, const CdbOptions &s2)
|
||||
{ return s1.compare(s2) != 0u; }
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // CDBSETTINGS_H
|
||||
@@ -1,224 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdboptionspage.h"
|
||||
#include "cdboptions.h"
|
||||
#include "debuggerconstants.h"
|
||||
#include "coreengine.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtGui/QMessageBox>
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
||||
static const char *dgbToolsDownloadLink32C = "http://www.microsoft.com/whdc/devtools/debugging/installx86.Mspx";
|
||||
static const char *dgbToolsDownloadLink64C = "http://www.microsoft.com/whdc/devtools/debugging/install64bit.Mspx";
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
static inline QString msgPathConfigNote()
|
||||
{
|
||||
#ifdef Q_OS_WIN64
|
||||
const bool is64bit = true;
|
||||
#else
|
||||
const bool is64bit = false;
|
||||
#endif
|
||||
const QString link = is64bit ? QLatin1String(dgbToolsDownloadLink64C) : QLatin1String(dgbToolsDownloadLink32C);
|
||||
//: Label text for path configuration. %2 is "x-bit version".
|
||||
return CdbOptionsPageWidget::tr(
|
||||
"<html><body><p>Specify the path to the "
|
||||
"<a href=\"%1\">Debugging Tools for Windows</a>"
|
||||
" (%2) here.</p>"
|
||||
"<p><b>Note:</b> Restarting Qt Creator is required for these settings to take effect.</p></p>"
|
||||
"</body></html>").arg(link, (is64bit ? CdbOptionsPageWidget::tr("64-bit version")
|
||||
: CdbOptionsPageWidget::tr("32-bit version")));
|
||||
}
|
||||
|
||||
CdbOptionsPageWidget::CdbOptionsPageWidget(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
m_ui.noteLabel->setText(msgPathConfigNote());
|
||||
m_ui.noteLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
connect(m_ui.noteLabel, SIGNAL(linkActivated(QString)), this, SLOT(downLoadLinkActivated(QString)));
|
||||
|
||||
m_ui.pathChooser->setExpectedKind(Utils::PathChooser::Directory);
|
||||
m_ui.pathChooser->addButton(tr("Autodetect"), this, SLOT(autoDetect()));
|
||||
m_ui.failureLabel->setVisible(false);
|
||||
}
|
||||
|
||||
|
||||
void CdbOptionsPageWidget::setOptions(CdbOptions &o)
|
||||
{
|
||||
m_ui.pathChooser->setPath(o.path);
|
||||
m_ui.cdbPathGroupBox->setChecked(o.enabled);
|
||||
m_ui.symbolPathListEditor->setPathList(o.symbolPaths);
|
||||
m_ui.sourcePathListEditor->setPathList(o.sourcePaths);
|
||||
m_ui.verboseSymbolLoadingCheckBox->setChecked(o.verboseSymbolLoading);
|
||||
m_ui.fastLoadDebuggingHelpersCheckBox->setChecked(o.fastLoadDebuggingHelpers);
|
||||
m_ui.breakOnExceptionCheckBox->setChecked(o.breakOnException);
|
||||
}
|
||||
|
||||
CdbOptions CdbOptionsPageWidget::options() const
|
||||
{
|
||||
CdbOptions rc;
|
||||
rc.path = m_ui.pathChooser->path();
|
||||
rc.enabled = m_ui.cdbPathGroupBox->isChecked();
|
||||
rc.symbolPaths = m_ui.symbolPathListEditor->pathList();
|
||||
rc.sourcePaths = m_ui.sourcePathListEditor->pathList();
|
||||
rc.verboseSymbolLoading = m_ui.verboseSymbolLoadingCheckBox->isChecked();
|
||||
rc.fastLoadDebuggingHelpers = m_ui.fastLoadDebuggingHelpersCheckBox->isChecked();
|
||||
rc.breakOnException = m_ui.breakOnExceptionCheckBox->isChecked();
|
||||
return rc;
|
||||
}
|
||||
|
||||
void CdbOptionsPageWidget::autoDetect()
|
||||
{
|
||||
QString path;
|
||||
QStringList checkedDirectories;
|
||||
const bool ok = CdbCore::CoreEngine::autoDetectPath(&path, &checkedDirectories);
|
||||
m_ui.cdbPathGroupBox->setChecked(ok);
|
||||
if (ok) {
|
||||
m_ui.pathChooser->setPath(path);
|
||||
} else {
|
||||
const QString msg = tr("\"Debugging Tools for Windows\" could not be found.");
|
||||
const QString details = tr("Checked:\n%1").arg(checkedDirectories.join(QString(QLatin1Char('\n'))));
|
||||
QMessageBox msbBox(QMessageBox::Information, tr("Autodetection"), msg, QMessageBox::Ok, this);
|
||||
msbBox.setDetailedText(details);
|
||||
msbBox.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void CdbOptionsPageWidget::setFailureMessage(const QString &msg)
|
||||
{
|
||||
m_ui.failureLabel->setText(msg);
|
||||
m_ui.failureLabel->setVisible(!msg.isEmpty());
|
||||
}
|
||||
|
||||
void CdbOptionsPageWidget::downLoadLinkActivated(const QString &link)
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl(link));
|
||||
}
|
||||
|
||||
QString CdbOptionsPageWidget::searchKeywords() const
|
||||
{
|
||||
QString rc;
|
||||
QTextStream(&rc) << m_ui.pathLabel->text() << ' ' << m_ui.symbolPathLabel->text()
|
||||
<< ' ' << m_ui.sourcePathLabel->text()
|
||||
<< ' ' << m_ui.verboseSymbolLoadingCheckBox->text()
|
||||
<< ' ' << m_ui.fastLoadDebuggingHelpersCheckBox->text()
|
||||
<< ' ' << m_ui.breakOnExceptionCheckBox->text();
|
||||
rc.remove(QLatin1Char('&'));
|
||||
return rc;
|
||||
}
|
||||
|
||||
// ---------- CdbOptionsPage
|
||||
CdbOptionsPage::CdbOptionsPage() :
|
||||
m_options(new CdbOptions)
|
||||
{
|
||||
m_options->fromSettings(Core::ICore::instance()->settings());
|
||||
}
|
||||
|
||||
CdbOptionsPage::~CdbOptionsPage()
|
||||
{
|
||||
}
|
||||
|
||||
QString CdbOptionsPage::settingsId()
|
||||
{
|
||||
return QLatin1String("F.Cdb");
|
||||
}
|
||||
|
||||
QString CdbOptionsPage::displayName() const
|
||||
{
|
||||
return tr("CDB");
|
||||
}
|
||||
|
||||
QString CdbOptionsPage::category() const
|
||||
{
|
||||
return QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
|
||||
}
|
||||
|
||||
QString CdbOptionsPage::displayCategory() const
|
||||
{
|
||||
return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_SETTINGS_TR_CATEGORY);
|
||||
}
|
||||
|
||||
QIcon CdbOptionsPage::categoryIcon() const
|
||||
{
|
||||
return QIcon(QLatin1String(Debugger::Constants::DEBUGGER_COMMON_SETTINGS_CATEGORY_ICON));
|
||||
}
|
||||
|
||||
QWidget *CdbOptionsPage::createPage(QWidget *parent)
|
||||
{
|
||||
m_widget = new CdbOptionsPageWidget(parent);
|
||||
m_widget->setOptions(*m_options);
|
||||
m_widget->setFailureMessage(m_failureMessage);
|
||||
if (m_searchKeywords.isEmpty())
|
||||
m_searchKeywords = m_widget->searchKeywords();
|
||||
return m_widget;
|
||||
}
|
||||
|
||||
void CdbOptionsPage::apply()
|
||||
{
|
||||
if (!m_widget)
|
||||
return;
|
||||
const CdbOptions newOptions = m_widget->options();
|
||||
if (unsigned changedMask = m_options->compare(newOptions)) {
|
||||
*m_options = newOptions;
|
||||
m_options->toSettings(Core::ICore::instance()->settings());
|
||||
// Paths changed?
|
||||
if (changedMask & CdbOptions::DebuggerPathsChanged) {
|
||||
emit debuggerPathsChanged();
|
||||
changedMask &= ~CdbOptions::DebuggerPathsChanged;
|
||||
}
|
||||
// Remaining options?
|
||||
if (changedMask)
|
||||
emit optionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void CdbOptionsPage::finish()
|
||||
{
|
||||
}
|
||||
|
||||
bool CdbOptionsPage::matches(const QString &s) const
|
||||
{
|
||||
return m_searchKeywords.contains(s, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -1,110 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBSETTINGSPAGE_H
|
||||
#define CDBSETTINGSPAGE_H
|
||||
|
||||
#include "cdboptions.h"
|
||||
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
#include "ui_cdboptionspagewidget.h"
|
||||
|
||||
#include <QtGui/QWidget>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class CdbOptionsPageWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CdbOptionsPageWidget(QWidget *parent);
|
||||
|
||||
void setOptions(CdbOptions &o);
|
||||
CdbOptions options() const;
|
||||
|
||||
void setFailureMessage(const QString &);
|
||||
|
||||
QString searchKeywords() const;
|
||||
|
||||
private slots:
|
||||
void autoDetect();
|
||||
void downLoadLinkActivated(const QString &);
|
||||
|
||||
private:
|
||||
Ui::CdbOptionsPageWidget m_ui;
|
||||
};
|
||||
|
||||
class CdbOptionsPage : public Core::IOptionsPage
|
||||
{
|
||||
Q_DISABLE_COPY(CdbOptionsPage)
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CdbOptionsPage();
|
||||
virtual ~CdbOptionsPage();
|
||||
|
||||
// IOptionsPage
|
||||
virtual QString id() const { return settingsId(); }
|
||||
virtual QString displayName() const;
|
||||
virtual QString category() const;
|
||||
virtual QString displayCategory() const;
|
||||
QIcon categoryIcon() const;
|
||||
|
||||
virtual QWidget *createPage(QWidget *parent);
|
||||
virtual void apply();
|
||||
virtual void finish();
|
||||
virtual bool matches(const QString &) const;
|
||||
|
||||
static QString settingsId();
|
||||
|
||||
// Load failure messages can be displayed here
|
||||
void setFailureMessage(const QString &msg) { m_failureMessage = msg; }
|
||||
QSharedPointer<CdbOptions> options() const { return m_options; }
|
||||
|
||||
signals:
|
||||
void debuggerPathsChanged();
|
||||
void optionsChanged();
|
||||
|
||||
private:
|
||||
const QSharedPointer<CdbOptions> m_options;
|
||||
QPointer<CdbOptionsPageWidget> m_widget;
|
||||
QString m_failureMessage;
|
||||
QString m_searchKeywords;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // CDBSETTINGSPAGE_H
|
||||
@@ -1,162 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CdbOptionsPageWidget</class>
|
||||
<widget class="QWidget" name="CdbOptionsPageWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>522</width>
|
||||
<height>512</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="cdbPathGroupBox">
|
||||
<property name="toolTip">
|
||||
<string>These options take effect at the next start of Qt Creator.</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string extracomment="Placeholder">CDB</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="pathLabel">
|
||||
<property name="text">
|
||||
<string>Path:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Utils::PathChooser" name="pathChooser" native="true"/>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="noteLabel">
|
||||
<property name="text">
|
||||
<string notr="true" extracomment="Placeholder">Note: bla, blah</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="pathGroupBox">
|
||||
<property name="title">
|
||||
<string>Debugger Paths</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="symbolPathLabel">
|
||||
<property name="text">
|
||||
<string>Symbol paths:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Debugger::Internal::CdbSymbolPathListEditor" name="symbolPathListEditor" native="true"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="sourcePathLabel">
|
||||
<property name="text">
|
||||
<string>Source paths:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Utils::PathListEditor" name="sourcePathListEditor" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="variousOptionsGroupBox">
|
||||
<property name="title">
|
||||
<string>Other Options</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_3">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="verboseSymbolLoadingCheckBox">
|
||||
<property name="text">
|
||||
<string>Verbose symbol loading</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="fastLoadDebuggingHelpersCheckBox">
|
||||
<property name="text">
|
||||
<string>Fast loading of debugging helpers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="breakOnExceptionCheckBox">
|
||||
<property name="text">
|
||||
<string>Break on exception</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>203</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="failureLabel">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: 'red';</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Utils::PathChooser</class>
|
||||
<extends>QWidget</extends>
|
||||
<header location="global">utils/pathchooser.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Utils::PathListEditor</class>
|
||||
<extends>QWidget</extends>
|
||||
<header location="global">utils/pathlisteditor.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Debugger::Internal::CdbSymbolPathListEditor</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>cdbsymbolpathlisteditor.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -1,426 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbstackframecontext.h"
|
||||
#include "cdbengine_p.h"
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
#include "cdbdumperhelper.h"
|
||||
#include "debuggeractions.h"
|
||||
#include "watchhandler.h"
|
||||
#include "coreengine.h"
|
||||
|
||||
#include <utils/savedaction.h>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
enum { OwnerNewItem, OwnerSymbolGroup, OwnerSymbolGroupDumper , OwnerDumper };
|
||||
|
||||
typedef QSharedPointer<CdbDumperHelper> SharedPointerCdbDumperHelper;
|
||||
typedef QList<WatchData> WatchDataList;
|
||||
|
||||
// Predicates for parametrizing the symbol group
|
||||
inline bool truePredicate(const WatchData & /* whatever */) { return true; }
|
||||
inline bool falsePredicate(const WatchData & /* whatever */) { return false; }
|
||||
inline bool isDumperPredicate(const WatchData &wd)
|
||||
{ return (wd.source & CdbStackFrameContext::SourceMask) == OwnerDumper; }
|
||||
|
||||
static inline void debugWatchDataList(const QList<WatchData> &l, const char *why = 0)
|
||||
{
|
||||
QDebug nospace = qDebug().nospace();
|
||||
if (why)
|
||||
nospace << why << '\n';
|
||||
foreach(const WatchData &wd, l)
|
||||
nospace << wd.toString() << '\n';
|
||||
nospace << '\n';
|
||||
}
|
||||
|
||||
// Match an item that is expanded in the watchhandler.
|
||||
class WatchHandlerExpandedPredicate {
|
||||
public:
|
||||
explicit inline WatchHandlerExpandedPredicate(const WatchHandler *wh) : m_wh(wh) {}
|
||||
inline bool operator()(const WatchData &wd) { return m_wh->isExpandedIName(wd.iname); }
|
||||
private:
|
||||
const WatchHandler *m_wh;
|
||||
};
|
||||
|
||||
// Match an item by iname
|
||||
class MatchINamePredicate {
|
||||
public:
|
||||
explicit inline MatchINamePredicate(const QString &iname) : m_iname(iname) {}
|
||||
inline bool operator()(const WatchData &wd) { return wd.iname == m_iname; }
|
||||
private:
|
||||
const QString &m_iname;
|
||||
};
|
||||
|
||||
// Put a sequence of WatchData into the model for the non-dumper case
|
||||
class WatchHandlerModelInserter {
|
||||
public:
|
||||
explicit WatchHandlerModelInserter(WatchHandler *wh) : m_wh(wh) {}
|
||||
|
||||
inline WatchHandlerModelInserter & operator*() { return *this; }
|
||||
inline WatchHandlerModelInserter &operator=(const WatchData &wd) {
|
||||
m_wh->insertData(wd);
|
||||
return *this;
|
||||
}
|
||||
inline WatchHandlerModelInserter &operator++() { return *this; }
|
||||
|
||||
private:
|
||||
WatchHandler *m_wh;
|
||||
};
|
||||
|
||||
// Put a sequence of WatchData into the model for the dumper case.
|
||||
// Sorts apart a sequence of WatchData using the Dumpers.
|
||||
// Puts the stuff for which there is no dumper in the model
|
||||
// as is and sets ownership to symbol group. The rest goes
|
||||
// to the dumpers. Additionally, checks for items pointing to a
|
||||
// dumpeable type and inserts a fake dereferenced item and value.
|
||||
class WatchHandleDumperInserter {
|
||||
public:
|
||||
explicit WatchHandleDumperInserter(WatchHandler *wh,
|
||||
const SharedPointerCdbDumperHelper &dumper);
|
||||
|
||||
inline WatchHandleDumperInserter & operator*() { return *this; }
|
||||
inline WatchHandleDumperInserter &operator=(WatchData &wd);
|
||||
inline WatchHandleDumperInserter &operator++() { return *this; }
|
||||
|
||||
private:
|
||||
bool expandPointerToDumpable(const WatchData &wd, QString *errorMessage);
|
||||
|
||||
const QRegExp m_hexNullPattern;
|
||||
WatchHandler *m_wh;
|
||||
const SharedPointerCdbDumperHelper m_dumper;
|
||||
QList<WatchData> m_dumperResult;
|
||||
};
|
||||
|
||||
WatchHandleDumperInserter::WatchHandleDumperInserter(WatchHandler *wh,
|
||||
const SharedPointerCdbDumperHelper &dumper) :
|
||||
m_hexNullPattern(QLatin1String("0x0+")),
|
||||
m_wh(wh),
|
||||
m_dumper(dumper)
|
||||
{
|
||||
Q_ASSERT(m_hexNullPattern.isValid());
|
||||
}
|
||||
|
||||
// Prevent recursion of the model by setting value and type
|
||||
static inline bool fixDumperType(WatchData *wd, const WatchData *source = 0)
|
||||
{
|
||||
const bool missing = wd->isTypeNeeded() || wd->type.isEmpty();
|
||||
if (missing) {
|
||||
static const QString unknownType = QCoreApplication::translate("CdbStackFrameContext", "<Unknown Type>");
|
||||
wd->setType(source ? source->type : unknownType, false);
|
||||
}
|
||||
return missing;
|
||||
}
|
||||
|
||||
static inline bool fixDumperValue(WatchData *wd, const WatchData *source = 0)
|
||||
{
|
||||
const bool missing = wd->isValueNeeded();
|
||||
if (missing) {
|
||||
if (source && source->isValueKnown()) {
|
||||
wd->setValue(source->value);
|
||||
} else {
|
||||
static const QString unknownValue = QCoreApplication::translate("CdbStackFrameContext", "<Unknown Value>");
|
||||
wd->setValue(unknownValue);
|
||||
}
|
||||
}
|
||||
return missing;
|
||||
}
|
||||
|
||||
// When querying an item, the queried item is sometimes returned in incomplete form.
|
||||
// Take over values from source.
|
||||
static inline void fixDumperResult(const WatchData &source,
|
||||
QList<WatchData> *result,
|
||||
bool suppressGrandChildren)
|
||||
{
|
||||
|
||||
const int size = result->size();
|
||||
if (!size)
|
||||
return;
|
||||
if (debugCDBWatchHandling)
|
||||
debugWatchDataList(*result, suppressGrandChildren ? ">fixDumperResult suppressGrandChildren" : ">fixDumperResult");
|
||||
WatchData &returned = result->front();
|
||||
if (returned.iname != source.iname)
|
||||
return;
|
||||
fixDumperType(&returned, &source);
|
||||
fixDumperValue(&returned, &source);
|
||||
// Indicate owner and known children
|
||||
returned.source = OwnerDumper;
|
||||
if (returned.isChildrenKnown() && returned.isHasChildrenKnown() && returned.hasChildren)
|
||||
returned.source |= CdbStackFrameContext::ChildrenKnownBit;
|
||||
if (size == 1)
|
||||
return;
|
||||
// If the model queries the expanding item by pretending childrenNeeded=1,
|
||||
// refuse the request as the children are already known
|
||||
returned.source |= CdbStackFrameContext::ChildrenKnownBit;
|
||||
// Fix the children: If the address is missing, we cannot query any further.
|
||||
const QList<WatchData>::iterator wend = result->end();
|
||||
QList<WatchData>::iterator it = result->begin();
|
||||
for (++it; it != wend; ++it) {
|
||||
WatchData &wd = *it;
|
||||
// Indicate owner and known children
|
||||
it->source = OwnerDumper;
|
||||
if (it->isChildrenKnown() && it->isHasChildrenKnown() && it->hasChildren)
|
||||
it->source |= CdbStackFrameContext::ChildrenKnownBit;
|
||||
// Cannot dump items with missing addresses or missing types
|
||||
const bool typeFixed = fixDumperType(&wd); // Order of evaluation!
|
||||
if ((wd.addr.isEmpty() && wd.isSomethingNeeded()) || typeFixed) {
|
||||
wd.setHasChildren(false);
|
||||
wd.setAllUnneeded();
|
||||
} else {
|
||||
// Hack: Suppress endless recursion of the model. To be fixed,
|
||||
// the model should not query non-visible items.
|
||||
if (suppressGrandChildren && (wd.isChildrenNeeded() || wd.isHasChildrenNeeded()))
|
||||
wd.setHasChildren(false);
|
||||
}
|
||||
}
|
||||
if (debugCDBWatchHandling)
|
||||
debugWatchDataList(*result, "<fixDumperResult");
|
||||
}
|
||||
|
||||
// Is this a non-null pointer to a dumpeable item with a value
|
||||
// "0x4343 class QString *" ? - Insert a fake '*' dereferenced item
|
||||
// and run dumpers on it. If that succeeds, insert the fake items owned by dumpers,
|
||||
// which will trigger the ignore predicate.
|
||||
// Note that the symbol context does not create '*' dereferenced items for
|
||||
// classes (see note in its header documentation).
|
||||
bool WatchHandleDumperInserter::expandPointerToDumpable(const WatchData &wd, QString *errorMessage)
|
||||
{
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << ">expandPointerToDumpable" << wd.toString();
|
||||
|
||||
bool handled = false;
|
||||
do {
|
||||
if (wd.error || !isPointerType(wd.type))
|
||||
break;
|
||||
const int classPos = wd.value.indexOf(" class ");
|
||||
if (classPos == -1)
|
||||
break;
|
||||
const QString hexAddrS = wd.value.mid(0, classPos);
|
||||
if (m_hexNullPattern.exactMatch(hexAddrS))
|
||||
break;
|
||||
const QString type = stripPointerType(wd.value.mid(classPos + 7));
|
||||
WatchData derefedWd;
|
||||
derefedWd.setType(type);
|
||||
derefedWd.setAddress(hexAddrS);
|
||||
derefedWd.name = QString(QLatin1Char('*'));
|
||||
derefedWd.iname = wd.iname + ".*";
|
||||
derefedWd.source = OwnerDumper | CdbStackFrameContext::ChildrenKnownBit;
|
||||
const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(derefedWd, true, &m_dumperResult, errorMessage);
|
||||
if (dr != CdbDumperHelper::DumpOk)
|
||||
break;
|
||||
fixDumperResult(derefedWd, &m_dumperResult, false);
|
||||
// Insert the pointer item with 1 additional child + its dumper results
|
||||
// Note: formal arguments might already be expanded in the symbol group.
|
||||
WatchData ptrWd = wd;
|
||||
ptrWd.source = OwnerDumper | CdbStackFrameContext::ChildrenKnownBit;
|
||||
ptrWd.setHasChildren(true);
|
||||
ptrWd.setChildrenUnneeded();
|
||||
m_wh->insertData(ptrWd);
|
||||
m_wh->insertBulkData(m_dumperResult);
|
||||
handled = true;
|
||||
} while (false);
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << "<expandPointerToDumpable returns " << handled << *errorMessage;
|
||||
return handled;
|
||||
}
|
||||
|
||||
WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd)
|
||||
{
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << "WatchHandleDumperInserter::operator=" << wd.toString();
|
||||
// Check pointer to dumpeable, dumpeable, insert accordingly.
|
||||
QString errorMessage;
|
||||
if (expandPointerToDumpable(wd, &errorMessage)) {
|
||||
// Nasty side effect: Modify owner for the ignore predicate
|
||||
wd.source = OwnerDumper;
|
||||
return *this;
|
||||
}
|
||||
// Expanded by internal dumper? : ok
|
||||
if ((wd.source & CdbStackFrameContext::SourceMask) == OwnerSymbolGroupDumper) {
|
||||
m_wh->insertData(wd);
|
||||
return *this;
|
||||
}
|
||||
// Try library dumpers.
|
||||
switch (m_dumper->dumpType(wd, true, &m_dumperResult, &errorMessage)) {
|
||||
case CdbDumperHelper::DumpOk:
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << "dumper triggered";
|
||||
// Dumpers omit types for complicated templates
|
||||
fixDumperResult(wd, &m_dumperResult, false);
|
||||
// Discard the original item and insert the dumper results
|
||||
m_wh->insertBulkData(m_dumperResult);
|
||||
// Nasty side effect: Modify owner for the ignore predicate
|
||||
wd.source = OwnerDumper;
|
||||
break;
|
||||
case CdbDumperHelper::DumpNotHandled:
|
||||
case CdbDumperHelper::DumpError:
|
||||
wd.source = OwnerSymbolGroup;
|
||||
m_wh->insertData(wd);
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// -----------CdbStackFrameContext
|
||||
CdbStackFrameContext::CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
CdbSymbolGroupContext *symbolContext) :
|
||||
m_useDumpers(dumper->isEnabled() && debuggerCore()->boolSetting(UseDebuggingHelpers)),
|
||||
m_dumper(dumper),
|
||||
m_symbolContext(symbolContext)
|
||||
{
|
||||
}
|
||||
|
||||
bool CdbStackFrameContext::assignValue(const QString &iname, const QString &value,
|
||||
QString *newValue /* = 0 */, QString *errorMessage)
|
||||
{
|
||||
return m_symbolContext->assignValue(iname, value, newValue, errorMessage);
|
||||
}
|
||||
|
||||
bool CdbStackFrameContext::populateModelInitially(WatchHandler *wh, QString *errorMessage)
|
||||
{
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << "populateModelInitially dumpers=" << m_useDumpers;
|
||||
// Recurse down items that are initially expanded in the view, stop processing for
|
||||
// dumper items.
|
||||
const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper);
|
||||
const bool rc = m_useDumpers ?
|
||||
CdbSymbolGroupContext::populateModelInitiallyHelper(rctx,
|
||||
WatchHandleDumperInserter(wh, m_dumper),
|
||||
WatchHandlerExpandedPredicate(wh),
|
||||
isDumperPredicate,
|
||||
errorMessage) :
|
||||
CdbSymbolGroupContext::populateModelInitiallyHelper(rctx,
|
||||
WatchHandlerModelInserter(wh),
|
||||
WatchHandlerExpandedPredicate(wh),
|
||||
falsePredicate,
|
||||
errorMessage);
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal,
|
||||
WatchHandler *wh,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << ">completeData src=" << incompleteLocal.source << incompleteLocal.toString();
|
||||
|
||||
const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper);
|
||||
// Expand symbol group items, recurse one level from desired item
|
||||
if (!m_useDumpers) {
|
||||
return CdbSymbolGroupContext::completeDataHelper(rctx, incompleteLocal,
|
||||
WatchHandlerModelInserter(wh),
|
||||
MatchINamePredicate(incompleteLocal.iname),
|
||||
falsePredicate,
|
||||
errorMessage);
|
||||
}
|
||||
|
||||
// Expand artifical dumper items
|
||||
if ((incompleteLocal.source & CdbStackFrameContext::SourceMask) == OwnerDumper) {
|
||||
// If the model queries the expanding item by pretending childrenNeeded=1,
|
||||
// refuse the request if the children are already known
|
||||
if (incompleteLocal.state == WatchData::ChildrenNeeded && (incompleteLocal.source & CdbStackFrameContext::ChildrenKnownBit)) {
|
||||
WatchData local = incompleteLocal;
|
||||
local.setChildrenUnneeded();
|
||||
wh->insertData(local);
|
||||
return true;
|
||||
}
|
||||
QList<WatchData> dumperResult;
|
||||
const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(incompleteLocal, true, &dumperResult, errorMessage);
|
||||
if (dr == CdbDumperHelper::DumpOk) {
|
||||
// Hack to stop endless model recursion
|
||||
const bool suppressGrandChildren = !wh->isExpandedIName(incompleteLocal.iname);
|
||||
fixDumperResult(incompleteLocal, &dumperResult, suppressGrandChildren);
|
||||
wh->insertBulkData(dumperResult);
|
||||
} else {
|
||||
const QString msg = QString::fromLatin1("Unable to further expand dumper watch data: '%1' (%2): %3/%4").arg(incompleteLocal.name, incompleteLocal.type).arg(int(dr)).arg(*errorMessage);
|
||||
qWarning("%s", qPrintable(msg));
|
||||
WatchData wd = incompleteLocal;
|
||||
if (wd.isValueNeeded())
|
||||
wd.setValue(QCoreApplication::translate("CdbStackFrameContext", "<Unknown>"));
|
||||
wd.setHasChildren(false);
|
||||
wd.setAllUnneeded();
|
||||
wh->insertData(wd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Expand symbol group items, recurse one level from desired item
|
||||
return CdbSymbolGroupContext::completeDataHelper(rctx, incompleteLocal,
|
||||
WatchHandleDumperInserter(wh, m_dumper),
|
||||
MatchINamePredicate(incompleteLocal.iname),
|
||||
isDumperPredicate,
|
||||
errorMessage);
|
||||
}
|
||||
|
||||
CdbStackFrameContext::~CdbStackFrameContext()
|
||||
{
|
||||
delete m_symbolContext;
|
||||
}
|
||||
|
||||
bool CdbStackFrameContext::editorToolTip(const QString &iname,
|
||||
QString *value,
|
||||
QString *errorMessage)
|
||||
{
|
||||
value->clear();
|
||||
unsigned long index;
|
||||
if (!m_symbolContext->lookupPrefix(iname, &index)) {
|
||||
*errorMessage = QString::fromLatin1("%1 not found.").arg(iname);
|
||||
return false;
|
||||
}
|
||||
// Check dumpers. Should actually be just one item.
|
||||
|
||||
WatchData wd;
|
||||
const unsigned rc = m_symbolContext->watchDataAt(index, &wd);
|
||||
if (m_useDumpers && !wd.error
|
||||
&& (0u == (rc & CdbCore::SymbolGroupContext::InternalDumperMask))
|
||||
&& m_dumper->state() != CdbDumperHelper::Disabled) {
|
||||
QList<WatchData> result;
|
||||
if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, &result, errorMessage)) {
|
||||
foreach (const WatchData &dwd, result) {
|
||||
if (!value->isEmpty())
|
||||
value->append(QLatin1Char('\n'));
|
||||
value->append(dwd.toToolTip());
|
||||
}
|
||||
return true;
|
||||
} // Dumped ok
|
||||
} // has Dumpers
|
||||
*value = wd.toToolTip();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -1,166 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbstacktracecontext.h"
|
||||
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
#include "cdbdumperhelper.h"
|
||||
#include "cdbengine_p.h"
|
||||
#include "debuggeractions.h"
|
||||
#include "debuggercore.h"
|
||||
#include "watchutils.h"
|
||||
#include "threadshandler.h"
|
||||
|
||||
#include <utils/savedaction.h>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QFileInfo>
|
||||
|
||||
enum { debug = 0 };
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
CdbStackTraceContext::CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper) :
|
||||
CdbCore::StackTraceContext(dumper->comInterfaces()),
|
||||
m_dumper(dumper)
|
||||
{
|
||||
}
|
||||
|
||||
CdbStackTraceContext *CdbStackTraceContext::create(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
QString *errorMessage)
|
||||
{
|
||||
CdbStackTraceContext *ctx = new CdbStackTraceContext(dumper);
|
||||
if (!ctx->init(UINT_MAX, errorMessage)) {
|
||||
delete ctx;
|
||||
return 0;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
CdbCore::SymbolGroupContext *
|
||||
CdbStackTraceContext::createSymbolGroup(const CdbCore::ComInterfaces & /* cif */,
|
||||
int index,
|
||||
const QString &prefix,
|
||||
CIDebugSymbolGroup *comSymbolGroup,
|
||||
QString *errorMessage)
|
||||
{
|
||||
// Exclude uninitialized variables if desired
|
||||
QStringList uninitializedVariables;
|
||||
const CdbCore::StackFrame &frame = stackFrameAt(index);
|
||||
if (debuggerCore()->action(UseCodeModel)->isChecked())
|
||||
getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
|
||||
frame.function, frame.fileName, frame.line, &uninitializedVariables);
|
||||
if (debug)
|
||||
qDebug() << frame << uninitializedVariables;
|
||||
CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(prefix,
|
||||
comSymbolGroup,
|
||||
m_dumper,
|
||||
uninitializedVariables,
|
||||
errorMessage);
|
||||
if (!sc) {
|
||||
*errorMessage = msgFrameContextFailed(index, frame, *errorMessage);
|
||||
return 0;
|
||||
}
|
||||
return sc;
|
||||
}
|
||||
|
||||
CdbSymbolGroupContext *CdbStackTraceContext::cdbSymbolGroupContextAt(int index, QString *errorMessage)
|
||||
{
|
||||
return static_cast<CdbSymbolGroupContext *>(symbolGroupContextAt(index, errorMessage));
|
||||
}
|
||||
|
||||
QList<StackFrame> CdbStackTraceContext::stackFrames() const
|
||||
{
|
||||
// Convert from Core data structures
|
||||
QList<StackFrame> rc;
|
||||
const int count = frameCount();
|
||||
for(int i = 0; i < count; i++) {
|
||||
const CdbCore::StackFrame &coreFrame = stackFrameAt(i);
|
||||
StackFrame frame;
|
||||
frame.level = i;
|
||||
frame.file = coreFrame.fileName;
|
||||
frame.usable = !frame.file.isEmpty() && QFileInfo(frame.file).isFile();
|
||||
frame.line = coreFrame.line;
|
||||
frame.function = coreFrame.function;
|
||||
frame.from = coreFrame.module;
|
||||
frame.address = coreFrame.address;
|
||||
rc.push_back(frame);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool CdbStackTraceContext::getThreads(const CdbCore::ComInterfaces &cif,
|
||||
bool stopped,
|
||||
Threads *threads,
|
||||
ULONG *currentThreadId,
|
||||
QString *errorMessage)
|
||||
{
|
||||
|
||||
QVector<CdbCore::Thread> coreThreads;
|
||||
if (!CdbCore::StackTraceContext::getThreadList(cif, &coreThreads, currentThreadId, errorMessage))
|
||||
return false;
|
||||
// Get frames only if stopped.
|
||||
QVector<CdbCore::StackFrame> frames;
|
||||
if (stopped)
|
||||
if (!CdbCore::StackTraceContext::getStoppedThreadFrames(cif, *currentThreadId,
|
||||
coreThreads, &frames, errorMessage))
|
||||
return false;
|
||||
// Convert from Core data structures
|
||||
threads->clear();
|
||||
const int count = coreThreads.size();
|
||||
if (!count)
|
||||
return true;
|
||||
threads->reserve(count);
|
||||
const QChar slash(QLatin1Char('/'));
|
||||
for (int i = 0; i < count; i++) {
|
||||
const CdbCore::Thread &coreThread = coreThreads.at(i);
|
||||
ThreadData data(coreThread.id);
|
||||
data.targetId = QLatin1String("0x") + QString::number(coreThread.systemId);
|
||||
data.name = coreThread.name;
|
||||
if (stopped) {
|
||||
const CdbCore::StackFrame &coreFrame = frames.at(i);
|
||||
data.address = coreFrame.address;
|
||||
data.function = coreFrame.function;
|
||||
data.lineNumber = coreFrame.line;
|
||||
// Basename only for brevity
|
||||
const int slashPos = coreFrame.fileName.lastIndexOf(slash);
|
||||
data.fileName = slashPos == -1 ? coreFrame.fileName : coreFrame.fileName.mid(slashPos + 1);
|
||||
}
|
||||
threads->push_back(data);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -1,101 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBSTACKTRACECONTEXT_H
|
||||
#define CDBSTACKTRACECONTEXT_H
|
||||
|
||||
#include "stackhandler.h"
|
||||
#include "stacktracecontext.h"
|
||||
#include "threadshandler.h"
|
||||
|
||||
#include "cdbcom.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextStream;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace CdbCore {
|
||||
struct ComInterfaces;
|
||||
}
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class CdbSymbolGroupContext;
|
||||
class CdbStackFrameContext;
|
||||
class CdbDumperHelper;
|
||||
struct ThreadData;
|
||||
|
||||
/* CdbStackTraceContext: Bridges CdbCore data structures and
|
||||
* Debugger structures and handles CdbSymbolGroupContext's. */
|
||||
|
||||
class CdbStackTraceContext : public CdbCore::StackTraceContext
|
||||
{
|
||||
Q_DISABLE_COPY(CdbStackTraceContext)
|
||||
|
||||
explicit CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper);
|
||||
|
||||
public:
|
||||
static CdbStackTraceContext *create(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
QString *errorMessage);
|
||||
|
||||
CdbSymbolGroupContext *cdbSymbolGroupContextAt(int index, QString *errorMessage);
|
||||
|
||||
QList<StackFrame> stackFrames() const;
|
||||
|
||||
// get threads in stopped state
|
||||
static bool getThreads(const CdbCore::ComInterfaces &cif,
|
||||
bool stopped,
|
||||
Threads *threads,
|
||||
ULONG *currentThreadId,
|
||||
QString *errorMessage);
|
||||
|
||||
protected:
|
||||
virtual CdbCore::SymbolGroupContext *createSymbolGroup(const CdbCore::ComInterfaces &cif,
|
||||
int index,
|
||||
const QString &prefix,
|
||||
CIDebugSymbolGroup *comSymbolGroup,
|
||||
QString *errorMessage);
|
||||
|
||||
private:
|
||||
const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // CDBSTACKTRACECONTEXT_H
|
||||
@@ -1,538 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
#include "cdbengine_p.h"
|
||||
#include "cdbdumperhelper.h"
|
||||
#include "watchhandler.h"
|
||||
#include "watchutils.h"
|
||||
#include "debuggeractions.h"
|
||||
#include "coreengine.h"
|
||||
#include "debuggercore.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QRegExp>
|
||||
|
||||
enum { debug = 0 };
|
||||
enum { debugInternalDumpers = 0 };
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
enum { OwnerNewItem, OwnerSymbolGroup, OwnerSymbolGroupDumper , OwnerDumper };
|
||||
|
||||
typedef QSharedPointer<CdbDumperHelper> SharedPointerCdbDumperHelper;
|
||||
typedef QList<WatchData> WatchDataList;
|
||||
|
||||
// Predicates for parametrizing the symbol group
|
||||
inline bool truePredicate(const WatchData & /* whatever */) { return true; }
|
||||
inline bool falsePredicate(const WatchData & /* whatever */) { return false; }
|
||||
inline bool isDumperPredicate(const WatchData &wd)
|
||||
{ return (wd.source & CdbSymbolGroupContext::SourceMask) == OwnerDumper; }
|
||||
|
||||
static inline void debugWatchDataList(const QList<WatchData> &l, const char *why = 0)
|
||||
{
|
||||
QDebug nospace = qDebug().nospace();
|
||||
if (why)
|
||||
nospace << why << '\n';
|
||||
foreach(const WatchData &wd, l)
|
||||
nospace << wd.toString() << '\n';
|
||||
nospace << '\n';
|
||||
}
|
||||
|
||||
// Match an item that is expanded in the watchhandler.
|
||||
class WatchHandlerExpandedPredicate {
|
||||
public:
|
||||
explicit inline WatchHandlerExpandedPredicate(const WatchHandler *wh) : m_wh(wh) {}
|
||||
inline bool operator()(const WatchData &wd) { return m_wh->isExpandedIName(wd.iname); }
|
||||
private:
|
||||
const WatchHandler *m_wh;
|
||||
};
|
||||
|
||||
// Match an item by iname
|
||||
class MatchINamePredicate {
|
||||
public:
|
||||
explicit inline MatchINamePredicate(const QString &iname) : m_iname(iname) {}
|
||||
inline bool operator()(const WatchData &wd) { return wd.iname == m_iname; }
|
||||
private:
|
||||
const QString &m_iname;
|
||||
};
|
||||
|
||||
// Put a sequence of WatchData into the model for the non-dumper case
|
||||
class WatchHandlerModelInserter {
|
||||
public:
|
||||
explicit WatchHandlerModelInserter(WatchHandler *wh) : m_wh(wh) {}
|
||||
|
||||
inline WatchHandlerModelInserter & operator*() { return *this; }
|
||||
inline WatchHandlerModelInserter &operator=(const WatchData &wd) {
|
||||
m_wh->insertData(wd);
|
||||
return *this;
|
||||
}
|
||||
inline WatchHandlerModelInserter &operator++() { return *this; }
|
||||
|
||||
private:
|
||||
WatchHandler *m_wh;
|
||||
};
|
||||
|
||||
// Put a sequence of WatchData into the model for the dumper case.
|
||||
// Sorts apart a sequence of WatchData using the Dumpers.
|
||||
// Puts the stuff for which there is no dumper in the model
|
||||
// as is and sets ownership to symbol group. The rest goes
|
||||
// to the dumpers. Additionally, checks for items pointing to a
|
||||
// dumpeable type and inserts a fake dereferenced item and value.
|
||||
class WatchHandleDumperInserter {
|
||||
public:
|
||||
explicit WatchHandleDumperInserter(WatchHandler *wh,
|
||||
const SharedPointerCdbDumperHelper &dumper);
|
||||
|
||||
inline WatchHandleDumperInserter & operator*() { return *this; }
|
||||
inline WatchHandleDumperInserter &operator=(WatchData &wd);
|
||||
inline WatchHandleDumperInserter &operator++() { return *this; }
|
||||
|
||||
private:
|
||||
bool expandPointerToDumpable(const WatchData &wd, QString *errorMessage);
|
||||
|
||||
const QRegExp m_hexNullPattern;
|
||||
WatchHandler *m_wh;
|
||||
const SharedPointerCdbDumperHelper m_dumper;
|
||||
QList<WatchData> m_dumperResult;
|
||||
};
|
||||
|
||||
WatchHandleDumperInserter::WatchHandleDumperInserter(WatchHandler *wh,
|
||||
const SharedPointerCdbDumperHelper &dumper) :
|
||||
m_hexNullPattern(QLatin1String("0x0+")),
|
||||
m_wh(wh),
|
||||
m_dumper(dumper)
|
||||
{
|
||||
Q_ASSERT(m_hexNullPattern.isValid());
|
||||
}
|
||||
|
||||
// Prevent recursion of the model by setting value and type
|
||||
static inline bool fixDumperType(WatchData *wd, const WatchData *source = 0)
|
||||
{
|
||||
const bool missing = wd->isTypeNeeded() || wd->type.isEmpty();
|
||||
if (missing) {
|
||||
static const QByteArray unknownType =
|
||||
QCoreApplication::translate("CdbSymbolGroupContext", "<Unknown Type>")
|
||||
.toUtf8();
|
||||
wd->setType(source ? source->type : unknownType, false);
|
||||
}
|
||||
return missing;
|
||||
}
|
||||
|
||||
static inline bool fixDumperValue(WatchData *wd, const WatchData *source = 0)
|
||||
{
|
||||
const bool missing = wd->isValueNeeded();
|
||||
if (missing) {
|
||||
if (source && source->isValueKnown()) {
|
||||
wd->setValue(source->value);
|
||||
} else {
|
||||
static const QString unknownValue = QCoreApplication::translate("CdbSymbolGroupContext", "<Unknown Value>");
|
||||
wd->setValue(unknownValue);
|
||||
}
|
||||
}
|
||||
return missing;
|
||||
}
|
||||
|
||||
// When querying an item, the queried item is sometimes returned in incomplete form.
|
||||
// Take over values from source.
|
||||
static inline void fixDumperResult(const WatchData &source,
|
||||
QList<WatchData> *result,
|
||||
bool suppressGrandChildren)
|
||||
{
|
||||
|
||||
const int size = result->size();
|
||||
if (!size)
|
||||
return;
|
||||
if (debugCDBWatchHandling)
|
||||
debugWatchDataList(*result, suppressGrandChildren ? ">fixDumperResult suppressGrandChildren" : ">fixDumperResult");
|
||||
WatchData &returned = result->front();
|
||||
if (returned.iname != source.iname)
|
||||
return;
|
||||
fixDumperType(&returned, &source);
|
||||
fixDumperValue(&returned, &source);
|
||||
// Indicate owner and known children
|
||||
returned.source = OwnerDumper;
|
||||
if (returned.isChildrenKnown() && returned.isHasChildrenKnown() && returned.hasChildren)
|
||||
returned.source |= CdbSymbolGroupContext::ChildrenKnownBit;
|
||||
if (size == 1)
|
||||
return;
|
||||
// If the model queries the expanding item by pretending childrenNeeded=1,
|
||||
// refuse the request as the children are already known
|
||||
returned.source |= CdbSymbolGroupContext::ChildrenKnownBit;
|
||||
// Fix the children: Assign sort id , if the address is missing, we cannot query any further.
|
||||
const int resultSize = result->size();
|
||||
for (int i = 1; i < resultSize; i++) {
|
||||
WatchData &wd = (*result)[i];
|
||||
wd.sortId = i;
|
||||
// Indicate owner and known children
|
||||
wd.source = OwnerDumper;
|
||||
if (wd.isChildrenKnown() && wd.isHasChildrenKnown() && wd.hasChildren)
|
||||
wd.source |= CdbSymbolGroupContext::ChildrenKnownBit;
|
||||
// Cannot dump items with missing addresses or missing types
|
||||
const bool typeFixed = fixDumperType(&wd); // Order of evaluation!
|
||||
if ((wd.address == 0 && wd.isSomethingNeeded()) || typeFixed) {
|
||||
wd.setHasChildren(false);
|
||||
wd.setAllUnneeded();
|
||||
} else {
|
||||
// Hack: Suppress endless recursion of the model. To be fixed,
|
||||
// the model should not query non-visible items.
|
||||
if (suppressGrandChildren && (wd.isChildrenNeeded() || wd.isHasChildrenNeeded()))
|
||||
wd.setHasChildren(false);
|
||||
}
|
||||
}
|
||||
if (debugCDBWatchHandling)
|
||||
debugWatchDataList(*result, "<fixDumperResult");
|
||||
}
|
||||
|
||||
// Is this a non-null pointer to a dumpeable item with a value
|
||||
// "0x4343 class QString *" ? - Insert a fake '*' dereferenced item
|
||||
// and run dumpers on it. If that succeeds, insert the fake items owned by dumpers,
|
||||
// which will trigger the ignore predicate.
|
||||
// Note that the symbol context does not create '*' dereferenced items for
|
||||
// classes (see note in its header documentation).
|
||||
bool WatchHandleDumperInserter::expandPointerToDumpable(const WatchData &wd, QString *errorMessage)
|
||||
{
|
||||
if (debugCDBWatchHandling > 1)
|
||||
qDebug() << ">expandPointerToDumpable" << wd.toString();
|
||||
|
||||
bool handled = false;
|
||||
do {
|
||||
// Must be a clas pointer item and a non-null-pointer.
|
||||
if (wd.error || !(wd.source & CdbSymbolGroupContext::ClassPointerBit)
|
||||
|| !isPointerType(wd.type))
|
||||
break;
|
||||
quint64 address = 0;
|
||||
if (!CdbCore::SymbolGroupContext::getUnsignedHexValue(wd.value, &address) || address == 0)
|
||||
break;
|
||||
const QByteArray type = stripPointerType(wd.type);
|
||||
WatchData derefedWd;
|
||||
derefedWd.setType(type);
|
||||
derefedWd.address = address;
|
||||
derefedWd.name = QString(QLatin1Char('*'));
|
||||
derefedWd.iname = wd.iname + ".*";
|
||||
derefedWd.source = OwnerDumper | CdbSymbolGroupContext::ChildrenKnownBit;
|
||||
const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(derefedWd, true, &m_dumperResult, errorMessage);
|
||||
if (dr != CdbDumperHelper::DumpOk)
|
||||
break;
|
||||
fixDumperResult(derefedWd, &m_dumperResult, false);
|
||||
// Insert the pointer item with 1 additional child + its dumper results
|
||||
// Note: formal arguments might already be expanded in the symbol group.
|
||||
WatchData ptrWd = wd;
|
||||
ptrWd.source = OwnerDumper | CdbSymbolGroupContext::ChildrenKnownBit;
|
||||
ptrWd.setHasChildren(true);
|
||||
ptrWd.setChildrenUnneeded();
|
||||
m_wh->insertData(ptrWd);
|
||||
m_wh->insertBulkData(m_dumperResult);
|
||||
handled = true;
|
||||
} while (false);
|
||||
if (debugCDBWatchHandling > 1)
|
||||
qDebug() << "<expandPointerToDumpable returns " << handled << *errorMessage;
|
||||
return handled;
|
||||
}
|
||||
|
||||
WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd)
|
||||
{
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << "WatchHandleDumperInserter::operator=" << wd.toString();
|
||||
// Check pointer to dumpeable, dumpeable, insert accordingly.
|
||||
QString errorMessage;
|
||||
if (expandPointerToDumpable(wd, &errorMessage)) {
|
||||
// Nasty side effect: Modify owner for the ignore predicate
|
||||
wd.source = OwnerDumper;
|
||||
return *this;
|
||||
}
|
||||
// Expanded by internal dumper? : ok
|
||||
if ((wd.source & CdbSymbolGroupContext::SourceMask) == OwnerSymbolGroupDumper) {
|
||||
m_wh->insertData(wd);
|
||||
return *this;
|
||||
}
|
||||
// Try library dumpers.
|
||||
switch (m_dumper->dumpType(wd, true, &m_dumperResult, &errorMessage)) {
|
||||
case CdbDumperHelper::DumpOk:
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << "dumper triggered";
|
||||
// Dumpers omit types for complicated templates
|
||||
fixDumperResult(wd, &m_dumperResult, false);
|
||||
// Discard the original item and insert the dumper results
|
||||
m_wh->insertBulkData(m_dumperResult);
|
||||
// Nasty side effect: Modify owner for the ignore predicate
|
||||
wd.source = OwnerDumper;
|
||||
break;
|
||||
case CdbDumperHelper::DumpNotHandled:
|
||||
case CdbDumperHelper::DumpError:
|
||||
wd.source = OwnerSymbolGroup;
|
||||
m_wh->insertData(wd);
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
CdbSymbolGroupRecursionContext::CdbSymbolGroupRecursionContext(CdbSymbolGroupContext *ctx,
|
||||
int ido) :
|
||||
context(ctx),
|
||||
internalDumperOwner(ido)
|
||||
{
|
||||
}
|
||||
|
||||
static inline CdbSymbolGroupContext::SymbolState getSymbolState(const DEBUG_SYMBOL_PARAMETERS &p)
|
||||
{
|
||||
if (p.SubElements == 0u)
|
||||
return CdbSymbolGroupContext::LeafSymbol;
|
||||
return (p.Flags & DEBUG_SYMBOL_EXPANDED) ?
|
||||
CdbSymbolGroupContext::ExpandedSymbol :
|
||||
CdbSymbolGroupContext::CollapsedSymbol;
|
||||
}
|
||||
|
||||
CdbSymbolGroupContext::CdbSymbolGroupContext(const QString &prefix,
|
||||
CIDebugSymbolGroup *symbolGroup,
|
||||
const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
const QStringList &uninitializedVariables) :
|
||||
CdbCore::SymbolGroupContext(prefix, symbolGroup,
|
||||
dumper->comInterfaces()->debugDataSpaces,
|
||||
uninitializedVariables),
|
||||
m_useDumpers(dumper->isEnabled() && debuggerCore()->boolSetting(UseDebuggingHelpers)),
|
||||
m_dumper(dumper)
|
||||
{
|
||||
setShadowedNameFormat(WatchData::shadowedNameFormat());
|
||||
}
|
||||
|
||||
CdbSymbolGroupContext *CdbSymbolGroupContext::create(const QString &prefix,
|
||||
CIDebugSymbolGroup *symbolGroup,
|
||||
const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
const QStringList &uninitializedVariables,
|
||||
QString *errorMessage)
|
||||
{
|
||||
CdbSymbolGroupContext *rc = new CdbSymbolGroupContext(prefix, symbolGroup,
|
||||
dumper,
|
||||
uninitializedVariables);
|
||||
if (!rc->init(errorMessage)) {
|
||||
delete rc;
|
||||
return 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Fix display values: Pass through strings, convert unsigned integers
|
||||
// to decimal ('0x5454`fedf'), remove inner templates from
|
||||
// "0x4343 class list<>".
|
||||
static inline QString fixValue(const QString &value, const QByteArray &type, bool *isClassPointer)
|
||||
{
|
||||
*isClassPointer = false;
|
||||
// Pass through complete strings. Note that char pointers
|
||||
// (0x00A "hallo") will be reformatted below.
|
||||
const QChar doubleQuote = QLatin1Char('"');
|
||||
if (value.startsWith(doubleQuote) && value.endsWith(doubleQuote))
|
||||
return value;
|
||||
// Real Integer numbers Unsigned hex numbers (0x)/decimal numbers (0n)
|
||||
if (type != "bool" && isIntType(type)) {
|
||||
const QVariant intValue = CdbCore::SymbolGroupContext::getIntValue(value);
|
||||
if (intValue.isValid())
|
||||
return intValue.toString();
|
||||
}
|
||||
/* Pointers: Fix the address and strip off lengthy class type specifications
|
||||
* "0x00000000`000045AA "hallo" -> "0x45AA "hallo"
|
||||
* "0x00000000`000045AA class Bla<std::....> *" -> "0x45AA" */
|
||||
if (isPointerType(type)) {
|
||||
quint64 ptrValue;
|
||||
int endPos;
|
||||
if (CdbCore::SymbolGroupContext::getUnsignedHexValue(value, &ptrValue, &endPos)) {
|
||||
QString fixedValue = QString::fromAscii("0x%1").arg(ptrValue, 0, 16);
|
||||
// What is the second token...
|
||||
if (endPos < value.size() - 1) {
|
||||
const QString token = value.mid(endPos + 1);
|
||||
if (token.startsWith(QLatin1String("class")) || token.startsWith(QLatin1String("struct"))) {
|
||||
*isClassPointer = true;
|
||||
} else {
|
||||
fixedValue += QLatin1Char(' ');
|
||||
fixedValue += token;
|
||||
}
|
||||
} // has token
|
||||
return fixedValue;
|
||||
} // pointer value conversion ok
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
unsigned CdbSymbolGroupContext::watchDataAt(unsigned long index, WatchData *wd)
|
||||
{
|
||||
ULONG typeId;
|
||||
ULONG64 address;
|
||||
QString iname;
|
||||
QString value;
|
||||
QString type;
|
||||
const unsigned rc = dumpValue(index, &iname, &(wd->name), &address,
|
||||
&typeId, &type, &value);
|
||||
wd->exp = wd->iname = iname.toLatin1();
|
||||
wd->setAddress(address);
|
||||
wd->setType(type.toUtf8(), false);
|
||||
if (rc & OutOfScope) {
|
||||
wd->setError(WatchData::msgNotInScope());
|
||||
} else {
|
||||
bool isClassPointer;
|
||||
wd->setValue(fixValue(value, type.toUtf8(), &isClassPointer));
|
||||
if (isClassPointer)
|
||||
wd->source |= CdbSymbolGroupContext::ClassPointerBit;
|
||||
const bool hasChildren = rc & HasChildren;
|
||||
wd->setHasChildren(hasChildren);
|
||||
if (hasChildren)
|
||||
wd->setChildrenNeeded();
|
||||
}
|
||||
if (debug > 1)
|
||||
qDebug() << "watchDataAt" << index << QString::number(rc, 16) << wd->toString();
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool CdbSymbolGroupContext::populateModelInitially(WatchHandler *wh, QString *errorMessage)
|
||||
{
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << ">populateModelInitially dumpers=" << m_useDumpers;
|
||||
// Recurse down items that are initially expanded in the view, stop processing for
|
||||
// dumper items.
|
||||
const CdbSymbolGroupRecursionContext rctx(this, OwnerSymbolGroupDumper);
|
||||
const bool rc = m_useDumpers ?
|
||||
populateModelInitiallyHelper(rctx,
|
||||
WatchHandleDumperInserter(wh, m_dumper),
|
||||
WatchHandlerExpandedPredicate(wh),
|
||||
isDumperPredicate,
|
||||
errorMessage) :
|
||||
populateModelInitiallyHelper(rctx,
|
||||
WatchHandlerModelInserter(wh),
|
||||
WatchHandlerExpandedPredicate(wh),
|
||||
falsePredicate,
|
||||
errorMessage);
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug("<populateModelInitially\n%s\n", qPrintable(toString()));
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool CdbSymbolGroupContext::completeData(const WatchData &incompleteLocal,
|
||||
WatchHandler *wh,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug(">completeData src=%d %s\n%s\n",
|
||||
incompleteLocal.source, qPrintable(incompleteLocal.toString()),
|
||||
qPrintable(toString()));
|
||||
|
||||
const CdbSymbolGroupRecursionContext rctx(this, OwnerSymbolGroupDumper);
|
||||
// Expand symbol group items, recurse one level from desired item
|
||||
if (!m_useDumpers) {
|
||||
return completeDataHelper(rctx, incompleteLocal,
|
||||
WatchHandlerModelInserter(wh),
|
||||
MatchINamePredicate(incompleteLocal.iname),
|
||||
falsePredicate,
|
||||
errorMessage);
|
||||
}
|
||||
|
||||
// Expand artifical dumper items
|
||||
if ((incompleteLocal.source & CdbSymbolGroupContext::SourceMask) == OwnerDumper) {
|
||||
// If the model queries the expanding item by pretending childrenNeeded=1,
|
||||
// refuse the request if the children are already known
|
||||
if (incompleteLocal.state == WatchData::ChildrenNeeded && (incompleteLocal.source & CdbSymbolGroupContext::ChildrenKnownBit)) {
|
||||
WatchData local = incompleteLocal;
|
||||
local.setChildrenUnneeded();
|
||||
wh->insertData(local);
|
||||
return true;
|
||||
}
|
||||
QList<WatchData> dumperResult;
|
||||
const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(incompleteLocal, true, &dumperResult, errorMessage);
|
||||
if (dr == CdbDumperHelper::DumpOk) {
|
||||
// Hack to stop endless model recursion
|
||||
const bool suppressGrandChildren = !wh->isExpandedIName(incompleteLocal.iname);
|
||||
fixDumperResult(incompleteLocal, &dumperResult, suppressGrandChildren);
|
||||
wh->insertBulkData(dumperResult);
|
||||
} else {
|
||||
const QString msg = QString::fromLatin1("Unable to further expand dumper watch data: '%1' (%2): %3/%4")
|
||||
.arg(incompleteLocal.name)
|
||||
.arg(QString::fromUtf8(incompleteLocal.type))
|
||||
.arg(int(dr)).arg(*errorMessage);
|
||||
qWarning("%s", qPrintable(msg));
|
||||
WatchData wd = incompleteLocal;
|
||||
if (wd.isValueNeeded())
|
||||
wd.setValue(QCoreApplication::translate("CdbSymbolGroupContext", "<Unknown>"));
|
||||
wd.setHasChildren(false);
|
||||
wd.setAllUnneeded();
|
||||
wh->insertData(wd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Expand symbol group items, recurse one level from desired item
|
||||
return completeDataHelper(rctx, incompleteLocal,
|
||||
WatchHandleDumperInserter(wh, m_dumper),
|
||||
MatchINamePredicate(incompleteLocal.iname),
|
||||
isDumperPredicate,
|
||||
errorMessage);
|
||||
}
|
||||
|
||||
bool CdbSymbolGroupContext::editorToolTip(const QString &iname,
|
||||
QString *value,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugToolTips)
|
||||
qDebug() << iname;
|
||||
value->clear();
|
||||
unsigned long index;
|
||||
if (!lookupPrefix(iname, &index)) {
|
||||
*errorMessage = QString::fromLatin1("%1 not found.").arg(iname);
|
||||
return false;
|
||||
}
|
||||
// Check dumpers. Should actually be just one item.
|
||||
|
||||
WatchData wd;
|
||||
const unsigned rc = watchDataAt(index, &wd);
|
||||
if (m_useDumpers && !wd.error
|
||||
&& (0u == (rc & CdbCore::SymbolGroupContext::InternalDumperMask))
|
||||
&& m_dumper->state() != CdbDumperHelper::Disabled) {
|
||||
QList<WatchData> result;
|
||||
if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, &result, errorMessage)) {
|
||||
foreach (const WatchData &dwd, result) {
|
||||
if (!value->isEmpty())
|
||||
value->append(QLatin1Char('\n'));
|
||||
value->append(dwd.toToolTip());
|
||||
}
|
||||
return true;
|
||||
} // Dumped ok
|
||||
} // has Dumpers
|
||||
if (debugToolTips)
|
||||
qDebug() << iname << wd.toString();
|
||||
*value = wd.toToolTip();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -1,173 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBSYMBOLGROUPCONTEXT_H
|
||||
#define CDBSYMBOLGROUPCONTEXT_H
|
||||
|
||||
#include "cdbcom.h"
|
||||
#include "symbolgroupcontext.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QPair>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QSet>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class WatchData;
|
||||
class WatchHandler;
|
||||
struct CdbSymbolGroupRecursionContext;
|
||||
class CdbDumperHelper;
|
||||
|
||||
|
||||
/* CdbSymbolGroupContext manages a symbol group context and
|
||||
* a dumper context. It dispatches calls between the local items
|
||||
* that are handled by the symbol group and those that are handled by the dumpers. */
|
||||
|
||||
class CdbSymbolGroupContext : public CdbCore::SymbolGroupContext
|
||||
{
|
||||
Q_DISABLE_COPY(CdbSymbolGroupContext);
|
||||
explicit CdbSymbolGroupContext(const QString &prefix,
|
||||
CIDebugSymbolGroup *symbolGroup,
|
||||
const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
const QStringList &uninitializedVariables);
|
||||
|
||||
public:
|
||||
// Mask bits for the source field of watch data.
|
||||
enum { SourceMask = 0xFF,
|
||||
// We know the children although the WatchModel does not believe us.
|
||||
ChildrenKnownBit = 0x0100,
|
||||
// Is a pointer to a potentially dumpeable class.
|
||||
ClassPointerBit = 0x0200 };
|
||||
|
||||
static CdbSymbolGroupContext *create(const QString &prefix,
|
||||
CIDebugSymbolGroup *symbolGroup,
|
||||
const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
const QStringList &uninitializedVariables,
|
||||
QString *errorMessage);
|
||||
|
||||
bool editorToolTip(const QString &iname, QString *value, QString *errorMessage);
|
||||
|
||||
bool populateModelInitially(WatchHandler *wh, QString *errorMessage);
|
||||
|
||||
bool completeData(const WatchData &incompleteLocal,
|
||||
WatchHandler *wh,
|
||||
QString *errorMessage);
|
||||
|
||||
private:
|
||||
// Initially populate the locals model for a new stackframe.
|
||||
// Write a sequence of WatchData to it, recurse down if the
|
||||
// recursionPredicate agrees. The ignorePredicate can be used
|
||||
// to terminate processing after insertion of an item (if the calling
|
||||
// routine wants to insert another subtree).
|
||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||
static bool populateModelInitiallyHelper(const CdbSymbolGroupRecursionContext &ctx,
|
||||
OutputIterator it,
|
||||
RecursionPredicate recursionPredicate,
|
||||
IgnorePredicate ignorePredicate,
|
||||
QString *errorMessage);
|
||||
|
||||
// Complete children of a WatchData item.
|
||||
// Write a sequence of WatchData to it, recurse if the
|
||||
// recursionPredicate agrees. The ignorePredicate can be used
|
||||
// to terminate processing after insertion of an item (if the calling
|
||||
// routine wants to insert another subtree).
|
||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||
static bool completeDataHelper (const CdbSymbolGroupRecursionContext &ctx,
|
||||
WatchData incompleteLocal,
|
||||
OutputIterator it,
|
||||
RecursionPredicate recursionPredicate,
|
||||
IgnorePredicate ignorePredicate,
|
||||
QString *errorMessage);
|
||||
|
||||
// Retrieve child symbols of prefix as a sequence of WatchData.
|
||||
template <class OutputIterator>
|
||||
bool getChildSymbols(const QString &prefix, OutputIterator it, QString *errorMessage)
|
||||
{ return getDumpChildSymbols(0, prefix, 0, it, errorMessage); }
|
||||
// Retrieve child symbols of prefix as a sequence of WatchData.
|
||||
// Is CIDebugDataSpaces is != 0, try internal dumper and set owner
|
||||
template <class OutputIterator>
|
||||
bool getDumpChildSymbols(const QString &prefix,
|
||||
int dumpedOwner,
|
||||
OutputIterator it, QString *errorMessage);
|
||||
|
||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||
static bool insertSymbolRecursion(WatchData wd,
|
||||
const CdbSymbolGroupRecursionContext &ctx,
|
||||
OutputIterator it,
|
||||
RecursionPredicate recursionPredicate,
|
||||
IgnorePredicate ignorePredicate,
|
||||
QString *errorMessage);
|
||||
|
||||
unsigned watchDataAt(unsigned long index, WatchData *);
|
||||
|
||||
const bool m_useDumpers;
|
||||
const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||
};
|
||||
|
||||
// A convenience struct to save parameters for the model recursion.
|
||||
struct CdbSymbolGroupRecursionContext {
|
||||
explicit CdbSymbolGroupRecursionContext(CdbSymbolGroupContext *ctx, int internalDumperOwner);
|
||||
|
||||
CdbSymbolGroupContext *context;
|
||||
int internalDumperOwner;
|
||||
};
|
||||
|
||||
// Helper to a sequence of WatchData into a list.
|
||||
class WatchDataBackInserter
|
||||
{
|
||||
public:
|
||||
explicit WatchDataBackInserter(QList<WatchData> &wh) : m_wh(wh) {}
|
||||
|
||||
inline WatchDataBackInserter & operator*() { return *this; }
|
||||
inline WatchDataBackInserter &operator=(const WatchData &wd) {
|
||||
m_wh.push_back(wd);
|
||||
return *this;
|
||||
}
|
||||
inline WatchDataBackInserter &operator++() { return *this; }
|
||||
|
||||
private:
|
||||
QList<WatchData> &m_wh;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#include "cdbsymbolgroupcontext_tpl.h"
|
||||
|
||||
#endif // CDBSYMBOLGROUPCONTEXT_H
|
||||
@@ -1,182 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBSYMBOLGROUPCONTEXT_TPL_H
|
||||
#define CDBSYMBOLGROUPCONTEXT_TPL_H
|
||||
|
||||
#include "watchhandler.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
enum { debugSgRecursion = 0 };
|
||||
|
||||
template <class OutputIterator>
|
||||
bool CdbSymbolGroupContext::getDumpChildSymbols(const QString &prefix,
|
||||
int dumpedOwner,
|
||||
OutputIterator it, QString *errorMessage)
|
||||
{
|
||||
unsigned long start;
|
||||
unsigned long parentId;
|
||||
if (!getChildSymbolsPosition(prefix, &start, &parentId, errorMessage))
|
||||
return false;
|
||||
// Skip over expanded children. Internal dumping might expand
|
||||
// children, so, re-evaluate size in end condition.
|
||||
// Note the that the internal dumpers might expand children,
|
||||
// so the size might change.
|
||||
int sortId = 0;
|
||||
for (int s = start; s < size(); ++s) {
|
||||
const DEBUG_SYMBOL_PARAMETERS &p = symbolParameterAt(s);
|
||||
if (p.ParentSymbol == parentId && isSymbolDisplayable(p)) {
|
||||
WatchData wd;
|
||||
const unsigned rc = watchDataAt(s, &wd);
|
||||
if (rc & InternalDumperMask)
|
||||
wd.source = dumpedOwner;
|
||||
wd.sortId = sortId++;
|
||||
*it = wd;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Insert a symbol (and its first level children depending on forceRecursion)
|
||||
// The parent symbol is inserted before the children to make dumper handling
|
||||
// simpler. In theory, it can happen that the symbol context indicates
|
||||
// children but can expand none, which would lead to invalid parent information
|
||||
// (expand icon), though (ignore for simplicity).
|
||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||
bool CdbSymbolGroupContext::insertSymbolRecursion(WatchData wd,
|
||||
const CdbSymbolGroupRecursionContext &ctx,
|
||||
OutputIterator it,
|
||||
RecursionPredicate recursionPredicate,
|
||||
IgnorePredicate ignorePredicate,
|
||||
QString *errorMessage)
|
||||
{
|
||||
// Find out whether to recurse (has children and predicate agrees)
|
||||
const bool hasChildren = wd.hasChildren || wd.isChildrenNeeded();
|
||||
const bool recurse = hasChildren && recursionPredicate(wd);
|
||||
if (debugSgRecursion)
|
||||
qDebug() << "insertSymbolRecursion" << '\n' << wd.iname << "recurse=" << recurse;
|
||||
if (!recurse) {
|
||||
// No further recursion at this level, pretend entry is complete
|
||||
// to the watchmodel for the parent to show (only remaining watchhandler-specific
|
||||
// part here).
|
||||
if (wd.isChildrenNeeded()) {
|
||||
wd.setHasChildren(true);
|
||||
wd.setChildrenUnneeded();
|
||||
}
|
||||
if (debugSgRecursion)
|
||||
qDebug() << " INSERTING non-recursive: " << wd.toString();
|
||||
*it = wd;
|
||||
++it;
|
||||
return true;
|
||||
}
|
||||
// Recursion: Indicate children unneeded
|
||||
wd.setHasChildren(true);
|
||||
wd.setChildrenUnneeded();
|
||||
if (debugSgRecursion)
|
||||
qDebug() << " INSERTING recursive: " << wd.toString();
|
||||
*it = wd;
|
||||
++it;
|
||||
// Recurse unless the predicate disagrees
|
||||
if (ignorePredicate(wd))
|
||||
return true;
|
||||
QList<WatchData> watchList;
|
||||
// This implicitly enforces expansion
|
||||
if (!ctx.context->getDumpChildSymbols(wd.iname,
|
||||
ctx.internalDumperOwner,
|
||||
WatchDataBackInserter(watchList), errorMessage))
|
||||
return false;
|
||||
const int childCount = watchList.size();
|
||||
for (int c = 0; c < childCount; c++) {
|
||||
const WatchData &cwd = watchList.at(c);
|
||||
if (wd.isValid()) { // We sometimes get empty names for deeply nested data
|
||||
if (!insertSymbolRecursion(cwd, ctx, it, recursionPredicate, ignorePredicate, errorMessage))
|
||||
return false;
|
||||
} else {
|
||||
const QString msg = QString::fromLatin1("WARNING: Skipping invalid child symbol #%2 (type %3) of '%4'.").
|
||||
arg(QLatin1String(Q_FUNC_INFO)).arg(c).arg(cwd.type, QString::fromLatin1(wd.iname));
|
||||
qWarning("%s\n", qPrintable(msg));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||
bool CdbSymbolGroupContext::populateModelInitiallyHelper(const CdbSymbolGroupRecursionContext &ctx,
|
||||
OutputIterator it,
|
||||
RecursionPredicate recursionPredicate,
|
||||
IgnorePredicate ignorePredicate,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugSgRecursion)
|
||||
qDebug() << "### CdbSymbolGroupContext::populateModelInitially";
|
||||
|
||||
// Insert root items
|
||||
QList<WatchData> watchList;
|
||||
CdbSymbolGroupContext *sg = ctx.context;
|
||||
if (!sg->getDumpChildSymbols(sg->prefix(),
|
||||
ctx.internalDumperOwner,
|
||||
WatchDataBackInserter(watchList), errorMessage))
|
||||
return false;
|
||||
// Insert data
|
||||
foreach(const WatchData &wd, watchList)
|
||||
if (!insertSymbolRecursion(wd, ctx, it, recursionPredicate, ignorePredicate, errorMessage))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||
bool CdbSymbolGroupContext::completeDataHelper(const CdbSymbolGroupRecursionContext &ctx,
|
||||
WatchData incompleteLocal,
|
||||
OutputIterator it,
|
||||
RecursionPredicate recursionPredicate,
|
||||
IgnorePredicate ignorePredicate,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugSgRecursion)
|
||||
qDebug().nospace() << "###>CdbSymbolGroupContext::completeData" << ' ' << incompleteLocal.iname << '\n';
|
||||
// If the symbols are already expanded in the context, they will be re-inserted,
|
||||
// which is not handled for simplicity.
|
||||
if (!insertSymbolRecursion(incompleteLocal, ctx, it, recursionPredicate, ignorePredicate, errorMessage))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // CDBSYMBOLGROUPCONTEXT_TPL_H
|
||||
@@ -1,532 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "corebreakpoint.h"
|
||||
#include "coreengine.h"
|
||||
#ifndef TEST_COMPILE // Usage in manual tests
|
||||
# include "dbgwinutils.h"
|
||||
#endif
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QMap>
|
||||
|
||||
enum { debugBP = 0 };
|
||||
|
||||
namespace CdbCore {
|
||||
|
||||
// The CDB breakpoint expression syntax is:
|
||||
// `foo.cpp:523`[ "condition"]
|
||||
// module!function[ "condition"]
|
||||
|
||||
static const char sourceFileQuoteC = '`';
|
||||
|
||||
BreakPoint::BreakPoint() :
|
||||
type(Code),
|
||||
lineNumber(-1),
|
||||
address(0),
|
||||
threadId(-1),
|
||||
ignoreCount(0),
|
||||
oneShot(false),
|
||||
enabled(true)
|
||||
{
|
||||
}
|
||||
|
||||
void BreakPoint::clear()
|
||||
{
|
||||
type = Code;
|
||||
ignoreCount = 0;
|
||||
threadId = -1;
|
||||
oneShot = false;
|
||||
enabled = true;
|
||||
clearExpressionData();
|
||||
}
|
||||
|
||||
void BreakPoint::clearExpressionData()
|
||||
{
|
||||
fileName.clear();
|
||||
condition.clear();
|
||||
funcName.clear();
|
||||
lineNumber = -1;
|
||||
address = 0;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug dbg, const BreakPoint &bp)
|
||||
{
|
||||
dbg.nospace() << bp.toString();
|
||||
return dbg;
|
||||
}
|
||||
|
||||
QString BreakPoint::toString() const
|
||||
{
|
||||
QString rc;
|
||||
QTextStream str(&rc);
|
||||
str << (type == BreakPoint::Code ? "Code " : "Data ");
|
||||
if (address) {
|
||||
str.setIntegerBase(16);
|
||||
str << "0x" << address << ' ';
|
||||
str.setIntegerBase(10);
|
||||
}
|
||||
if (!fileName.isEmpty()) {
|
||||
str << "fileName='" << fileName << ':' << lineNumber << '\'';
|
||||
} else {
|
||||
str << "funcName='" << funcName << '\'';
|
||||
}
|
||||
if (threadId >= 0)
|
||||
str << " thread=" << threadId;
|
||||
if (!condition.isEmpty())
|
||||
str << " condition='" << condition << '\'';
|
||||
if (ignoreCount)
|
||||
str << " ignoreCount=" << ignoreCount;
|
||||
str << (enabled ? " enabled" : " disabled");
|
||||
if (oneShot)
|
||||
str << " oneShot";
|
||||
return rc;
|
||||
}
|
||||
|
||||
QString BreakPoint::expression() const
|
||||
{
|
||||
// format the breakpoint expression (file/function and condition)
|
||||
QString rc;
|
||||
QTextStream str(&rc);
|
||||
do {
|
||||
if (address) {
|
||||
str.setIntegerBase(16);
|
||||
str << "0x" << address;
|
||||
str.setIntegerBase(10);
|
||||
break;
|
||||
}
|
||||
if (!fileName.isEmpty()) {
|
||||
const QChar sourceFileQuote = QLatin1Char(sourceFileQuoteC);
|
||||
str << sourceFileQuote << QDir::toNativeSeparators(fileName) << QLatin1Char(':')
|
||||
<< lineNumber << sourceFileQuote;
|
||||
break;
|
||||
}
|
||||
if (!funcName.isEmpty()) {
|
||||
str << funcName;
|
||||
break;
|
||||
}
|
||||
} while (false);
|
||||
if (!condition.isEmpty())
|
||||
str << " \"" << condition << '"';
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline QString msgCannotSetBreakpoint(const QString &exp, const QString &why)
|
||||
{
|
||||
return QString::fromLatin1("Unable to set breakpoint '%1' : %2").arg(exp, why);
|
||||
}
|
||||
|
||||
bool BreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const
|
||||
{
|
||||
const QString expr = expression();
|
||||
if (debugBP)
|
||||
qDebug() << Q_FUNC_INFO << *this << expr;
|
||||
HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast<PCWSTR>(expr.utf16()));
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotSetBreakpoint(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr));
|
||||
return false;
|
||||
}
|
||||
// Pass Count is ignoreCount + 1
|
||||
hr = ibp->SetPassCount(ignoreCount + 1u);
|
||||
if (FAILED(hr))
|
||||
qWarning("Error setting passcount %d %s ", ignoreCount, qPrintable(expr));
|
||||
// Set up size for data breakpoints
|
||||
if (type == Data) {
|
||||
const ULONG size = 1u;
|
||||
hr = ibp->SetDataParameters(size, DEBUG_BREAK_READ | DEBUG_BREAK_WRITE);
|
||||
if (FAILED(hr)) {
|
||||
const QString msg = QString::fromLatin1("Cannot set watch size to %1: %2").
|
||||
arg(size).arg(CdbCore::msgComFailed("SetDataParameters", hr));
|
||||
*errorMessage = msgCannotSetBreakpoint(expr, msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Thread
|
||||
if (threadId >= 0) {
|
||||
hr = ibp->SetMatchThreadId(threadId);
|
||||
if (FAILED(hr)) {
|
||||
const QString msg = QString::fromLatin1("Cannot set thread id to %1: %2").
|
||||
arg(threadId).arg(CdbCore::msgComFailed("SetMatchThreadId", hr));
|
||||
*errorMessage = msgCannotSetBreakpoint(expr, msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Flags
|
||||
ULONG flags = 0;
|
||||
if (enabled)
|
||||
flags |= DEBUG_BREAKPOINT_ENABLED;
|
||||
if (oneShot)
|
||||
flags |= DEBUG_BREAKPOINT_ONE_SHOT;
|
||||
hr = ibp->AddFlags(flags);
|
||||
if (FAILED(hr)) {
|
||||
const QString msg = QString::fromLatin1("Cannot set flags to 0x%1: %2").
|
||||
arg(flags, 0 ,16).arg(CdbCore::msgComFailed("AddFlags", hr));
|
||||
*errorMessage = msgCannotSetBreakpoint(expr, msg);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline QString msgCannotAddBreakPoint(const QString &why)
|
||||
{
|
||||
return QString::fromLatin1("Unable to add breakpoint: %1").arg(why);
|
||||
}
|
||||
|
||||
bool BreakPoint::add(CIDebugControl* debugControl,
|
||||
QString *errorMessage,
|
||||
unsigned long *id,
|
||||
quint64 *address) const
|
||||
{
|
||||
CIDebugBreakpoint* ibp = 0;
|
||||
if (address)
|
||||
*address = 0;
|
||||
if (id)
|
||||
*id = 0;
|
||||
const ULONG iType = type == Code ? DEBUG_BREAKPOINT_CODE : DEBUG_BREAKPOINT_DATA;
|
||||
HRESULT hr = debugControl->AddBreakpoint2(iType, DEBUG_ANY_ID, &ibp);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("AddBreakpoint2", hr));
|
||||
return false;
|
||||
}
|
||||
if (!ibp) {
|
||||
*errorMessage = msgCannotAddBreakPoint(QLatin1String("<Unknown error>"));
|
||||
return false;
|
||||
}
|
||||
if (!apply(ibp, errorMessage))
|
||||
return false;
|
||||
// GetOffset can fail when attaching to remote processes, ignore return
|
||||
if (address) {
|
||||
hr = ibp->GetOffset(address);
|
||||
if (FAILED(hr))
|
||||
*address = 0;
|
||||
}
|
||||
if (id) {
|
||||
hr = ibp->GetId(id);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("GetId", hr));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Make sure file can be found in editor manager and text markers
|
||||
// Use '/', correct case and capitalize drive letter. Use a cache.
|
||||
|
||||
typedef QHash<QString, QString> NormalizedFileCache;
|
||||
Q_GLOBAL_STATIC(NormalizedFileCache, normalizedFileNameCache)
|
||||
|
||||
QString BreakPoint::normalizeFileName(const QString &f)
|
||||
{
|
||||
#ifdef TEST_COMPILE // Usage in manual tests
|
||||
return f;
|
||||
#else
|
||||
QTC_ASSERT(!f.isEmpty(), return f)
|
||||
const NormalizedFileCache::const_iterator it = normalizedFileNameCache()->constFind(f);
|
||||
if (it != normalizedFileNameCache()->constEnd())
|
||||
return it.value();
|
||||
QString normalizedName = QDir::fromNativeSeparators(Debugger::Internal::winNormalizeFileName(f));
|
||||
// Upcase drive letter for consistency even if case mapping fails.
|
||||
if (normalizedName.size() > 2 && normalizedName.at(1) == QLatin1Char(':'))
|
||||
normalizedName[0] = normalizedName.at(0).toUpper();
|
||||
normalizedFileNameCache()->insert(f, normalizedName);
|
||||
return normalizedName;
|
||||
#endif
|
||||
}
|
||||
|
||||
void BreakPoint::clearNormalizeFileNameCache()
|
||||
{
|
||||
normalizedFileNameCache()->clear();
|
||||
}
|
||||
|
||||
static inline QString msgCannotRetrieveBreakpoint(const QString &why)
|
||||
{
|
||||
return QString::fromLatin1("Cannot retrieve breakpoint: %1").arg(why);
|
||||
}
|
||||
|
||||
static inline int threadIdOfBreakpoint(CIDebugBreakpoint *ibp)
|
||||
{
|
||||
// Thread: E_NOINTERFACE indicates no thread has been set.
|
||||
int threadId = -1;
|
||||
ULONG iThreadId;
|
||||
if (S_OK == ibp->GetMatchThreadId(&iThreadId))
|
||||
threadId = iThreadId;
|
||||
return threadId;
|
||||
}
|
||||
|
||||
bool BreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
|
||||
{
|
||||
clear();
|
||||
// Get type
|
||||
ULONG iType;
|
||||
ULONG processorType;
|
||||
HRESULT hr = ibp->GetType(&iType, &processorType);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotRetrieveBreakpoint(CdbCore::msgComFailed("GetType", hr));
|
||||
return false;
|
||||
}
|
||||
type = iType == DEBUG_BREAKPOINT_CODE ? Code : Data;
|
||||
// Get & parse expression
|
||||
WCHAR wszBuf[MAX_PATH];
|
||||
hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotRetrieveBreakpoint(CdbCore::msgComFailed("GetOffsetExpressionWide", hr));
|
||||
return false;
|
||||
}
|
||||
threadId = threadIdOfBreakpoint(ibp);
|
||||
// Pass Count is ignoreCount + 1
|
||||
ibp->GetPassCount(&ignoreCount);
|
||||
if (ignoreCount)
|
||||
ignoreCount--;
|
||||
ULONG flags = 0;
|
||||
ibp->GetFlags(&flags);
|
||||
oneShot = (flags & DEBUG_BREAKPOINT_ONE_SHOT);
|
||||
enabled = (flags & DEBUG_BREAKPOINT_ENABLED);
|
||||
const QString expr = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
|
||||
if (!parseExpression(expr)) {
|
||||
*errorMessage = QString::fromLatin1("Parsing of '%1' failed.").arg(expr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BreakPoint::parseExpression(const QString &expr)
|
||||
{
|
||||
clearExpressionData();
|
||||
const QChar sourceFileQuote = QLatin1Char(sourceFileQuoteC);
|
||||
// Check for file or function
|
||||
int conditionPos = 0;
|
||||
if (expr.startsWith(QLatin1String("0x"))) { // Check address token
|
||||
conditionPos = expr.indexOf(QLatin1Char(' '));
|
||||
QString addressS;
|
||||
if (conditionPos != -1) {
|
||||
addressS = expr.mid(2, conditionPos - 2);
|
||||
conditionPos++;
|
||||
} else {
|
||||
addressS = expr.mid(2);
|
||||
conditionPos = expr.size();
|
||||
}
|
||||
addressS.remove(QLatin1Char('\'')); // 64bit separator
|
||||
bool ok;
|
||||
address = addressS.toULongLong(&ok, 16);
|
||||
if (!ok)
|
||||
return false;
|
||||
} else if (expr.startsWith(sourceFileQuote)) { // `c:\foo.cpp:523`[ "condition"]
|
||||
// Do not fall for the drive letter colon here
|
||||
const int colonPos = expr.indexOf(QLatin1Char(':'), 3);
|
||||
if (colonPos == -1)
|
||||
return false;
|
||||
conditionPos = expr.indexOf(sourceFileQuote, colonPos + 1);
|
||||
if (conditionPos == -1)
|
||||
return false;
|
||||
fileName = normalizeFileName(expr.mid(1, colonPos - 1));
|
||||
const QString lineNumberS = expr.mid(colonPos + 1, conditionPos - colonPos - 1);
|
||||
bool lineNumberOk = false;
|
||||
lineNumber = lineNumberS.toInt(&lineNumberOk);
|
||||
if (!lineNumberOk)
|
||||
return false;
|
||||
conditionPos++;
|
||||
} else {
|
||||
// Check function token
|
||||
conditionPos = expr.indexOf(QLatin1Char(' '));
|
||||
if (conditionPos != -1) {
|
||||
funcName = expr.mid(0, conditionPos);
|
||||
conditionPos++;
|
||||
} else {
|
||||
funcName = expr;
|
||||
conditionPos = expr.size();
|
||||
}
|
||||
}
|
||||
// Condition? ".if bla"
|
||||
if (conditionPos >= expr.size())
|
||||
return true;
|
||||
const QChar doubleQuote = QLatin1Char('"');
|
||||
conditionPos = expr.indexOf(doubleQuote, conditionPos);
|
||||
if (conditionPos == -1)
|
||||
return true;
|
||||
conditionPos++;
|
||||
const int condEndPos = expr.lastIndexOf(doubleQuote);
|
||||
if (condEndPos == -1)
|
||||
return false;
|
||||
condition = expr.mid(conditionPos, condEndPos - conditionPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BreakPoint::getBreakPointCount(CIDebugControl* debugControl, ULONG *count, QString *errorMessage /* = 0*/)
|
||||
{
|
||||
const HRESULT hr = debugControl->GetNumberBreakpoints(count);
|
||||
if (FAILED(hr)) {
|
||||
if (errorMessage)
|
||||
*errorMessage = QString::fromLatin1("Cannot determine breakpoint count: %1").
|
||||
arg(CdbCore::msgComFailed("GetNumberBreakpoints", hr));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BreakPoint::getBreakPoints(CIDebugControl* debugControl, QList<BreakPoint> *bps, QString *errorMessage)
|
||||
{
|
||||
ULONG count = 0;
|
||||
bps->clear();
|
||||
if (!getBreakPointCount(debugControl, &count, errorMessage))
|
||||
return false;
|
||||
// retrieve one by one and parse
|
||||
for (ULONG b = 0; b < count; b++) {
|
||||
CIDebugBreakpoint *ibp = 0;
|
||||
const HRESULT hr = debugControl->GetBreakpointByIndex2(b, &ibp);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint %1: %2").
|
||||
arg(b).arg(CdbCore::msgComFailed("GetBreakpointByIndex2", hr));
|
||||
return false;
|
||||
}
|
||||
BreakPoint bp;
|
||||
if (!bp.retrieve(ibp, errorMessage))
|
||||
return false;
|
||||
bps->push_back(bp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find a breakpoint by id
|
||||
static inline QString msgNoBreakPointWithId(unsigned long id, const QString &why)
|
||||
{
|
||||
return QString::fromLatin1("Unable to find breakpoint with id %1: %2").arg(id).arg(why);
|
||||
}
|
||||
|
||||
CIDebugBreakpoint *BreakPoint::breakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage)
|
||||
{
|
||||
CIDebugBreakpoint *ibp = 0;
|
||||
const HRESULT hr = ctl->GetBreakpointById2(id, &ibp);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgNoBreakPointWithId(id, CdbCore::msgComFailed("GetBreakpointById2", hr));
|
||||
return 0;
|
||||
}
|
||||
if (!ibp) {
|
||||
*errorMessage = msgNoBreakPointWithId(id, QLatin1String("<not found>"));
|
||||
return 0;
|
||||
}
|
||||
return ibp;
|
||||
}
|
||||
|
||||
// Remove breakpoint by id
|
||||
bool BreakPoint::removeBreakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage)
|
||||
{
|
||||
if (debugBP)
|
||||
qDebug() << Q_FUNC_INFO << id;
|
||||
CIDebugBreakpoint *ibp = breakPointById(ctl, id, errorMessage);
|
||||
if (!ibp)
|
||||
return false;
|
||||
const HRESULT hr = ctl->RemoveBreakpoint2(ibp);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = QString::fromLatin1("Cannot remove breakpoint %1: %2").arg(id).arg(CdbCore::msgComFailed("RemoveBreakpointById2", hr));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set enabled by id
|
||||
|
||||
// Change enabled state of a breakpoint by id
|
||||
static inline QString msgCannotSetBreakPointEnabled(unsigned long id, bool enabled, const QString &why)
|
||||
{
|
||||
return QString::fromLatin1("Cannot %1 breakpoint %2: %3").
|
||||
arg(QLatin1String(enabled ? "enable" : "disable")).arg(id).arg(why);
|
||||
}
|
||||
|
||||
bool BreakPoint::setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool enabled, QString *errorMessage)
|
||||
{
|
||||
if (debugBP)
|
||||
qDebug() << Q_FUNC_INFO << id << enabled;
|
||||
CIDebugBreakpoint *ibp = breakPointById(ctl, id, errorMessage);
|
||||
if (!ibp) {
|
||||
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, *errorMessage);
|
||||
return false;
|
||||
}
|
||||
// Compare flags
|
||||
ULONG flags;
|
||||
HRESULT hr = ibp->GetFlags(&flags);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, CdbCore::msgComFailed("GetFlags", hr));
|
||||
return false;
|
||||
}
|
||||
const bool wasEnabled = (flags & DEBUG_BREAKPOINT_ENABLED);
|
||||
if (wasEnabled == enabled)
|
||||
return true;
|
||||
// Set new value
|
||||
if (enabled) {
|
||||
flags |= DEBUG_BREAKPOINT_ENABLED;
|
||||
} else {
|
||||
flags &= ~DEBUG_BREAKPOINT_ENABLED;
|
||||
}
|
||||
hr = ibp->SetFlags(flags);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, CdbCore::msgComFailed("SetFlags", hr));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Change thread-id of a breakpoint
|
||||
static inline QString msgCannotSetBreakPointThread(unsigned long id, int tid, const QString &why)
|
||||
{
|
||||
return QString::fromLatin1("Cannot set breakpoint %1 thread to %2: %3").arg(id).arg(tid).arg(why);
|
||||
}
|
||||
|
||||
bool BreakPoint::setBreakPointThreadById(CIDebugControl *ctl, unsigned long id, int threadId, QString *errorMessage)
|
||||
{
|
||||
if (debugBP)
|
||||
qDebug() << Q_FUNC_INFO << id << threadId;
|
||||
CIDebugBreakpoint *ibp = breakPointById(ctl, id, errorMessage);
|
||||
if (!ibp) {
|
||||
*errorMessage = msgCannotSetBreakPointThread(id, threadId, *errorMessage);
|
||||
return false;
|
||||
}
|
||||
// Compare thread ids
|
||||
const int oldThreadId = threadIdOfBreakpoint(ibp);
|
||||
if (oldThreadId == threadId)
|
||||
return true;
|
||||
const ULONG newIThreadId = threadId == -1 ? DEBUG_ANY_ID : static_cast<ULONG>(threadId);
|
||||
if (debugBP)
|
||||
qDebug() << "Changing thread id of " << id << " from " << oldThreadId << " to " << threadId
|
||||
<< '(' << newIThreadId << ')';
|
||||
const HRESULT hr = ibp->SetMatchThreadId(newIThreadId);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotSetBreakPointThread(id, threadId, *errorMessage);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace CdbCore
|
||||
@@ -1,113 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBCOREBREAKPOINTS_H
|
||||
#define CDBCOREBREAKPOINTS_H
|
||||
|
||||
#include "cdbcom.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QList>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QDebug;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace CdbCore {
|
||||
|
||||
/* CDB Break point data structure with utilities to
|
||||
* apply to engine and to retrieve them from the engine and comparison.
|
||||
* Can stop on 'sourcefile:line', function or address.
|
||||
* When/How many times it triggers can be influenced by
|
||||
* condition/ignorecount and 'oneshot'-flag. */
|
||||
|
||||
// Note: File is named corebreakpoint.h/cpp to avoid conflicts with
|
||||
// ../breakpoint.h/cpp.
|
||||
|
||||
struct BreakPoint
|
||||
{
|
||||
enum Type { Code, // Stop in code.
|
||||
Data // Stop when accessing address.
|
||||
};
|
||||
|
||||
BreakPoint();
|
||||
|
||||
void clear();
|
||||
void clearExpressionData();
|
||||
|
||||
QString expression() const;
|
||||
|
||||
// Apply parameters (with the exception of type, which is
|
||||
// passed as a parameter to IDebugControl within add().
|
||||
bool apply(CIDebugBreakpoint *ibp, QString *errorMessage) const;
|
||||
// Convenience to add to a IDebugControl4.
|
||||
bool add(CIDebugControl* debugControl,
|
||||
QString *errorMessage,
|
||||
unsigned long *id = 0,
|
||||
quint64 *address = 0) const;
|
||||
|
||||
// Retrieve/parse breakpoints from the interfaces
|
||||
bool retrieve(CIDebugBreakpoint *ibp, QString *errorMessage);
|
||||
bool parseExpression(const QString &expr);
|
||||
// Retrieve all breakpoints from the engine
|
||||
static bool getBreakPointCount(CIDebugControl* debugControl, ULONG *count, QString *errorMessage = 0);
|
||||
static bool getBreakPoints(CIDebugControl* debugControl, QList<BreakPoint> *bps, QString *errorMessage);
|
||||
// Control helpers
|
||||
static CIDebugBreakpoint *breakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage);
|
||||
static bool removeBreakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage);
|
||||
static bool setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool enabled, QString *errorMessage);
|
||||
static bool setBreakPointThreadById(CIDebugControl *ctl, unsigned long id, int threadId, QString *errorMessage);
|
||||
|
||||
// Return a 'canonical' file (using '/' and capitalized drive letter)
|
||||
static QString normalizeFileName(const QString &f);
|
||||
static void clearNormalizeFileNameCache();
|
||||
QString toString() const;
|
||||
|
||||
Type type;
|
||||
QString fileName; // short name of source file
|
||||
int lineNumber; // line in source file
|
||||
QString funcName; // name of containing function
|
||||
quint64 address;
|
||||
|
||||
int threadId;
|
||||
QString condition; // condition associated with breakpoint
|
||||
unsigned long ignoreCount; // ignore count associated with breakpoint
|
||||
bool oneShot;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug, const BreakPoint &bp);
|
||||
|
||||
} // namespace CdbCore
|
||||
|
||||
#endif // CDBCOREBREAKPOINTS_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,230 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef COREENGINE_H
|
||||
#define COREENGINE_H
|
||||
|
||||
#include "cdbcom.h"
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
namespace CdbCore {
|
||||
|
||||
class DebugOutputBase;
|
||||
class DebugEventCallbackBase;
|
||||
|
||||
// helper struct to pass interfaces around
|
||||
struct ComInterfaces
|
||||
{
|
||||
ComInterfaces();
|
||||
|
||||
CIDebugClient* debugClient;
|
||||
CIDebugControl* debugControl;
|
||||
CIDebugSystemObjects* debugSystemObjects;
|
||||
CIDebugSymbols* debugSymbols;
|
||||
CIDebugRegisters* debugRegisters;
|
||||
CIDebugDataSpaces* debugDataSpaces;
|
||||
CIDebugAdvanced* debugAdvanced;
|
||||
};
|
||||
|
||||
class CoreEngine : public QObject
|
||||
{
|
||||
Q_DISABLE_COPY(CoreEngine)
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ExpressionSyntax { AssemblerExpressionSyntax, CppExpressionSyntax };
|
||||
enum CodeLevel { CodeLevelAssembly, CodeLevelSource };
|
||||
|
||||
typedef QSharedPointer<DebugOutputBase> DebugOutputBasePtr;
|
||||
typedef QSharedPointer<DebugEventCallbackBase> DebugEventCallbackBasePtr;
|
||||
|
||||
explicit CoreEngine(QObject *parent = 0);
|
||||
virtual ~CoreEngine();
|
||||
|
||||
// Preliminary release interfaces.
|
||||
void releaseInterfaces();
|
||||
bool hasInterfaces() const;
|
||||
static bool interfacesAvailable();
|
||||
|
||||
bool init(const QString &dllEnginePath, QString *errorMessage);
|
||||
// code level/output
|
||||
|
||||
inline const ComInterfaces &interfaces() const { return m_cif; }
|
||||
|
||||
// Set handlers
|
||||
DebugOutputBasePtr setDebugOutput(const DebugOutputBasePtr &);
|
||||
DebugEventCallbackBasePtr setDebugEventCallback(const DebugEventCallbackBasePtr &);
|
||||
|
||||
// Start functions
|
||||
bool startDebuggerWithExecutable(const QString &workingDirectory,
|
||||
const QString &filename,
|
||||
const QString &args,
|
||||
const QStringList &env,
|
||||
QString *errorMessage);
|
||||
|
||||
bool startAttachDebugger(qint64 pid, bool suppressInitialBreakPoint,
|
||||
QString *errorMessage);
|
||||
|
||||
ULONG executionStatus() const;
|
||||
bool setExecutionStatus(ULONG ex, QString *errorMessage);
|
||||
|
||||
// break & interrupt
|
||||
bool debugBreakProcess(HANDLE hProcess, QString *errorMessage);
|
||||
// Currently does not interrupt debuggee
|
||||
bool setInterrupt(QString *errorMessage);
|
||||
|
||||
// Helpers for terminating the debuggees and ending the session
|
||||
bool detachCurrentProcess(QString *appendableErrorMessage);
|
||||
bool terminateCurrentProcess(QString *appendableErrorMessage);
|
||||
bool terminateProcesses(QString *appendableErrorMessage);
|
||||
bool endSession(QString *appendableErrorMessage);
|
||||
|
||||
// Watch timer: Listen for debug events and emit watchTimerDebugEvent() should one
|
||||
// occur.
|
||||
void startWatchTimer();
|
||||
void killWatchTimer();
|
||||
inline bool isWatchTimerRunning() const { return m_watchTimer != -1; }
|
||||
// Synchronous wait
|
||||
HRESULT waitForEvent(int timeOutMS);
|
||||
|
||||
// Commands and expressions
|
||||
bool executeDebuggerCommand(const QString &command, QString *errorMessage);
|
||||
|
||||
bool evaluateExpression(const QString &expression,
|
||||
DEBUG_VALUE *debugValue,
|
||||
QString *errorMessage);
|
||||
bool evaluateExpression(const QString &expression, QString *value,
|
||||
QString *type /* =0 */, QString *errorMessage);
|
||||
|
||||
// Path getters/setters
|
||||
QStringList sourcePaths() const;
|
||||
bool setSourcePaths(const QStringList &s, QString *errorMessage);
|
||||
QStringList symbolPaths() const;
|
||||
bool setSymbolPaths(const QStringList &s, QString *errorMessage);
|
||||
|
||||
bool isVerboseSymbolLoading() const;
|
||||
bool setVerboseSymbolLoading(bool v);
|
||||
|
||||
// Options
|
||||
ExpressionSyntax expressionSyntax() const;
|
||||
ExpressionSyntax setExpressionSyntax(ExpressionSyntax es);
|
||||
|
||||
CodeLevel codeLevel() const;
|
||||
CodeLevel setCodeLevel(CodeLevel);
|
||||
|
||||
QString dbengDLL() const { return m_dbengDLL; }
|
||||
|
||||
// Debuggee memory conveniences
|
||||
bool allocDebuggeeMemory(int size, ULONG64 *address, QString *errorMessage);
|
||||
bool createDebuggeeAscIIString(const QString &s, ULONG64 *address, QString *errorMessage);
|
||||
bool writeToDebuggee(const QByteArray &buffer, quint64 address, QString *errorMessage);
|
||||
// Write to debuggee memory in chunks
|
||||
bool disassemble(ULONG64 offset, unsigned long beforeLines, unsigned long afterLines,
|
||||
QString *target, QString *errorMessage);
|
||||
|
||||
quint64 getSourceLineAddress(const QString &file, int line, QString *errorMessage) const;
|
||||
|
||||
static bool autoDetectPath(QString *outPath,
|
||||
QStringList *checkedDirectories = 0);
|
||||
|
||||
unsigned moduleCount() const;
|
||||
|
||||
bool setBreakOnThrow(bool b, QString *errorMessage);
|
||||
bool setExceptionCommands(ULONG code,
|
||||
ULONG executionCommand,
|
||||
ULONG continueCommand,
|
||||
QString *errorMessage);
|
||||
|
||||
QString eventFilterStatus() const;
|
||||
|
||||
signals:
|
||||
void watchTimerDebugEvent();
|
||||
|
||||
// Emitted in the first time-out of the event handler in which
|
||||
// the number of modules no longer changes. Can be used as a
|
||||
// "startup" signal due to lack of a reliable "startup" detection
|
||||
// feature of the engine.
|
||||
void modulesLoaded();
|
||||
|
||||
public slots:
|
||||
void outputVersion();
|
||||
|
||||
protected:
|
||||
virtual void timerEvent(QTimerEvent* te);
|
||||
|
||||
private:
|
||||
void setModuleCount(unsigned m);
|
||||
void resetModuleLoadTimer();
|
||||
|
||||
static CoreEngine *m_instance;
|
||||
ComInterfaces m_cif;
|
||||
DebugOutputBasePtr m_debugOutput;
|
||||
DebugEventCallbackBasePtr m_debugEventCallback;
|
||||
QString m_dbengDLL;
|
||||
int m_watchTimer;
|
||||
unsigned m_moduleCount;
|
||||
unsigned m_lastTimerModuleCount;
|
||||
bool m_modulesLoadedEmitted;
|
||||
};
|
||||
|
||||
// Utility messages
|
||||
QString msgDebugEngineComResult(HRESULT hr);
|
||||
QString msgComFailed(const char *func, HRESULT hr);
|
||||
QString msgDebuggerCommandFailed(const QString &command, HRESULT hr);
|
||||
const char *msgExecutionStatusString(ULONG executionStatus);
|
||||
|
||||
// A class that sets an expression syntax on the debug control while in scope.
|
||||
// Can be nested as it checks for the old value.
|
||||
class SyntaxSetter {
|
||||
Q_DISABLE_COPY(SyntaxSetter)
|
||||
public:
|
||||
explicit inline SyntaxSetter(CoreEngine *engine, CoreEngine::ExpressionSyntax es) :
|
||||
m_oldSyntax(engine->setExpressionSyntax(es)),
|
||||
m_engine(engine) {}
|
||||
inline ~SyntaxSetter() { m_engine->setExpressionSyntax(m_oldSyntax); }
|
||||
|
||||
private:
|
||||
const CoreEngine::ExpressionSyntax m_oldSyntax;
|
||||
CoreEngine *m_engine;
|
||||
};
|
||||
|
||||
// Helpers to convert DEBUG_VALUE structs. The optional control is required to
|
||||
// convert large floating values.
|
||||
QString debugValueToString(const DEBUG_VALUE &dv, QString *qType =0, int integerBase = 10, CIDebugControl *ctl = 0);
|
||||
bool debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value);
|
||||
|
||||
} // namespace CdbCore
|
||||
|
||||
#endif // COREENGINE_H
|
||||
@@ -1,253 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "debugeventcallbackbase.h"
|
||||
#include "coreengine.h"
|
||||
|
||||
namespace CdbCore {
|
||||
|
||||
// DebugEventCallbackBase
|
||||
DebugEventCallbackBase::DebugEventCallbackBase()
|
||||
{
|
||||
}
|
||||
|
||||
DebugEventCallbackBase::~DebugEventCallbackBase()
|
||||
{
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::QueryInterface(
|
||||
THIS_
|
||||
IN REFIID InterfaceId,
|
||||
OUT PVOID* Interface)
|
||||
{
|
||||
*Interface = NULL;
|
||||
|
||||
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
|
||||
IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks))) {
|
||||
*Interface = (IDebugOutputCallbacks *)this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
} else {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) DebugEventCallbackBase::AddRef(THIS)
|
||||
{
|
||||
// This class is designed to be static so
|
||||
// there's no true refcount.
|
||||
return 1;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) DebugEventCallbackBase::Release(THIS)
|
||||
{
|
||||
// This class is designed to be static so
|
||||
// there's no true refcount.
|
||||
return 0;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
STDMETHODIMP DebugEventCallbackBase::Exception(
|
||||
THIS_
|
||||
__in PEXCEPTION_RECORD64,
|
||||
__in ULONG /* FirstChance */
|
||||
)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::CreateThread(
|
||||
THIS_
|
||||
__in ULONG64 /* Handle */,
|
||||
__in ULONG64 /* DataOffset */,
|
||||
__in ULONG64 /* StartOffset */
|
||||
)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::ExitThread(
|
||||
THIS_
|
||||
__in ULONG /* ExitCode */
|
||||
)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::CreateProcess(
|
||||
THIS_
|
||||
__in ULONG64 /* ImageFileHandle */,
|
||||
__in ULONG64 /* Handle */,
|
||||
__in ULONG64 /* BaseOffset */,
|
||||
__in ULONG /* ModuleSize */,
|
||||
__in_opt PCWSTR /* ModuleName */,
|
||||
__in_opt PCWSTR /* ImageName */,
|
||||
__in ULONG /* CheckSum */,
|
||||
__in ULONG /* TimeDateStamp */,
|
||||
__in ULONG64 /* InitialThreadHandle */,
|
||||
__in ULONG64 /* ThreadDataOffset */,
|
||||
__in ULONG64 /* StartOffset */
|
||||
)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::ExitProcess(
|
||||
THIS_
|
||||
__in ULONG /* ExitCode */
|
||||
)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::LoadModule(
|
||||
THIS_
|
||||
__in ULONG64 /* ImageFileHandle */,
|
||||
__in ULONG64 /* BaseOffset */,
|
||||
__in ULONG /* ModuleSize */,
|
||||
__in_opt PCWSTR /* ModuleName */,
|
||||
__in_opt PCWSTR /* ImageName */,
|
||||
__in ULONG /* CheckSum */,
|
||||
__in ULONG /* TimeDateStamp */
|
||||
)
|
||||
{
|
||||
handleModuleLoad();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::UnloadModule(
|
||||
THIS_
|
||||
__in_opt PCWSTR /* ImageBaseName */,
|
||||
__in ULONG64 /* BaseOffset */
|
||||
)
|
||||
{
|
||||
handleModuleUnload();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::SystemError(
|
||||
THIS_
|
||||
__in ULONG /* Error */,
|
||||
__in ULONG /* Level */
|
||||
)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::SessionStatus(
|
||||
THIS_
|
||||
__in ULONG /* Status */
|
||||
)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::ChangeDebuggeeState(
|
||||
THIS_
|
||||
__in ULONG /* Flags */,
|
||||
__in ULONG64 /* Argument */
|
||||
)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::ChangeEngineState(
|
||||
THIS_
|
||||
__in ULONG /* Flags */,
|
||||
__in ULONG64 /* Argument */
|
||||
)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallbackBase::ChangeSymbolState(
|
||||
THIS_
|
||||
__in ULONG /* Flags */,
|
||||
__in ULONG64 /* Argument */
|
||||
)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IDebugEventCallbacksWide *DebugEventCallbackBase::getEventCallback(CIDebugClient *clnt)
|
||||
{
|
||||
IDebugEventCallbacksWide *rc = 0;
|
||||
if (SUCCEEDED(clnt->GetEventCallbacksWide(&rc)))
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DebugEventCallbackBase::handleModuleLoad()
|
||||
{
|
||||
m_moduleCount++;
|
||||
}
|
||||
|
||||
void DebugEventCallbackBase::handleModuleUnload()
|
||||
{
|
||||
m_moduleCount--;
|
||||
}
|
||||
|
||||
unsigned DebugEventCallbackBase::moduleCount() const
|
||||
{
|
||||
return m_moduleCount;
|
||||
}
|
||||
|
||||
void DebugEventCallbackBase::setModuleCount(unsigned m)
|
||||
{
|
||||
m_moduleCount = m;
|
||||
}
|
||||
|
||||
ULONG DebugEventCallbackBase::baseInterestMask() const
|
||||
{
|
||||
return DEBUG_EVENT_LOAD_MODULE | DEBUG_EVENT_UNLOAD_MODULE;
|
||||
}
|
||||
|
||||
// ----------- EventCallbackRedirector
|
||||
|
||||
EventCallbackRedirector::EventCallbackRedirector(CoreEngine *engine,
|
||||
const DebugEventCallbackBasePtr &cb) :
|
||||
m_engine(engine),
|
||||
m_oldCallback(engine->setDebugEventCallback(cb))
|
||||
{
|
||||
}
|
||||
|
||||
EventCallbackRedirector::~EventCallbackRedirector()
|
||||
{
|
||||
m_engine->setDebugEventCallback(m_oldCallback);
|
||||
}
|
||||
|
||||
|
||||
} // namespace CdbCore
|
||||
@@ -1,197 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef DEBUGEVENTCALLBACKBASE_H
|
||||
#define DEBUGEVENTCALLBACKBASE_H
|
||||
|
||||
#include "cdbcom.h"
|
||||
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
namespace CdbCore {
|
||||
|
||||
class CoreEngine;
|
||||
|
||||
// Base class for event callbacks that takes care
|
||||
// Active X magic. Provides base implementations with
|
||||
// the exception of GetInterestMask(). The base class
|
||||
// needs to do some book-keeping on the modules loaded to
|
||||
// be able to detect the startup/completed attach of a
|
||||
// debuggee (see CoreEngine::modulesLoaded()).
|
||||
// So, the interest mask must be at least baseInterestMask()
|
||||
// and handleModuleLoad/Unload must be called from derived
|
||||
// classes when overwriting the handlers.
|
||||
class DebugEventCallbackBase : public IDebugEventCallbacksWide
|
||||
{
|
||||
protected:
|
||||
DebugEventCallbackBase();
|
||||
public:
|
||||
virtual ~DebugEventCallbackBase();
|
||||
// IUnknown.
|
||||
STDMETHOD(QueryInterface)(
|
||||
THIS_
|
||||
IN REFIID InterfaceId,
|
||||
OUT PVOID* Interface
|
||||
);
|
||||
STDMETHOD_(ULONG, AddRef)(
|
||||
THIS
|
||||
);
|
||||
STDMETHOD_(ULONG, Release)(
|
||||
THIS
|
||||
);
|
||||
|
||||
// IDebugEventCallbacks.
|
||||
|
||||
STDMETHOD(Breakpoint)(
|
||||
THIS_
|
||||
__in PDEBUG_BREAKPOINT2 Bp
|
||||
);
|
||||
|
||||
STDMETHOD(Exception)(
|
||||
THIS_
|
||||
__in PEXCEPTION_RECORD64 Exception,
|
||||
__in ULONG FirstChance
|
||||
);
|
||||
|
||||
STDMETHOD(CreateThread)(
|
||||
THIS_
|
||||
__in ULONG64 Handle,
|
||||
__in ULONG64 DataOffset,
|
||||
__in ULONG64 StartOffset
|
||||
);
|
||||
STDMETHOD(ExitThread)(
|
||||
THIS_
|
||||
__in ULONG ExitCode
|
||||
);
|
||||
|
||||
STDMETHOD(CreateProcess)(
|
||||
THIS_
|
||||
__in ULONG64 ImageFileHandle,
|
||||
__in ULONG64 Handle,
|
||||
__in ULONG64 BaseOffset,
|
||||
__in ULONG ModuleSize,
|
||||
__in_opt PCWSTR ModuleName,
|
||||
__in_opt PCWSTR ImageName,
|
||||
__in ULONG CheckSum,
|
||||
__in ULONG TimeDateStamp,
|
||||
__in ULONG64 InitialThreadHandle,
|
||||
__in ULONG64 ThreadDataOffset,
|
||||
__in ULONG64 StartOffset
|
||||
);
|
||||
|
||||
STDMETHOD(ExitProcess)(
|
||||
THIS_
|
||||
__in ULONG ExitCode
|
||||
);
|
||||
|
||||
// Call handleModuleLoad() when reimplementing this
|
||||
STDMETHOD(LoadModule)(
|
||||
THIS_
|
||||
__in ULONG64 ImageFileHandle,
|
||||
__in ULONG64 BaseOffset,
|
||||
__in ULONG ModuleSize,
|
||||
__in_opt PCWSTR ModuleName,
|
||||
__in_opt PCWSTR ImageName,
|
||||
__in ULONG CheckSum,
|
||||
__in ULONG TimeDateStamp
|
||||
);
|
||||
|
||||
// Call handleModuleUnload() when reimplementing this
|
||||
STDMETHOD(UnloadModule)(
|
||||
THIS_
|
||||
__in_opt PCWSTR ImageBaseName,
|
||||
__in ULONG64 BaseOffset
|
||||
);
|
||||
|
||||
STDMETHOD(SystemError)(
|
||||
THIS_
|
||||
__in ULONG Error,
|
||||
__in ULONG Level
|
||||
);
|
||||
|
||||
STDMETHOD(SessionStatus)(
|
||||
THIS_
|
||||
__in ULONG Status
|
||||
);
|
||||
|
||||
STDMETHOD(ChangeDebuggeeState)(
|
||||
THIS_
|
||||
__in ULONG Flags,
|
||||
__in ULONG64 Argument
|
||||
);
|
||||
|
||||
STDMETHOD(ChangeEngineState)(
|
||||
THIS_
|
||||
__in ULONG Flags,
|
||||
__in ULONG64 Argument
|
||||
);
|
||||
|
||||
STDMETHOD(ChangeSymbolState)(
|
||||
THIS_
|
||||
__in ULONG Flags,
|
||||
__in ULONG64 Argument
|
||||
);
|
||||
|
||||
|
||||
static IDebugEventCallbacksWide *getEventCallback(CIDebugClient *clnt);
|
||||
|
||||
unsigned moduleCount() const;
|
||||
void setModuleCount(unsigned m);
|
||||
|
||||
protected:
|
||||
void handleModuleLoad();
|
||||
void handleModuleUnload();
|
||||
ULONG baseInterestMask() const;
|
||||
|
||||
private:
|
||||
unsigned m_moduleCount;
|
||||
};
|
||||
|
||||
// Utility class to temporarily redirect events to another handler
|
||||
// as long as in scope
|
||||
class EventCallbackRedirector {
|
||||
Q_DISABLE_COPY(EventCallbackRedirector)
|
||||
public:
|
||||
typedef QSharedPointer<DebugEventCallbackBase> DebugEventCallbackBasePtr;
|
||||
|
||||
explicit EventCallbackRedirector(CoreEngine *engine, const DebugEventCallbackBasePtr &cb);
|
||||
~EventCallbackRedirector();
|
||||
|
||||
private:
|
||||
CoreEngine *m_engine;
|
||||
const DebugEventCallbackBasePtr m_oldCallback;
|
||||
};
|
||||
|
||||
} // namespace CdbCore
|
||||
|
||||
#endif // DEBUGEVENTCALLBACKBASE_H
|
||||
@@ -1,138 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "debugoutputbase.h"
|
||||
#include "coreengine.h"
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
namespace CdbCore {
|
||||
|
||||
DebugOutputBase::DebugOutputBase()
|
||||
{
|
||||
}
|
||||
|
||||
DebugOutputBase::~DebugOutputBase() // must be present to avoid exit crashes
|
||||
{
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugOutputBase::QueryInterface(
|
||||
THIS_
|
||||
IN REFIID InterfaceId,
|
||||
OUT PVOID* Interface
|
||||
)
|
||||
{
|
||||
*Interface = NULL;
|
||||
|
||||
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
|
||||
IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacksWide)))
|
||||
{
|
||||
*Interface = (IDebugOutputCallbacksWide*)this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
} else {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) DebugOutputBase::AddRef(THIS)
|
||||
{
|
||||
// This class is designed to be static so
|
||||
// there's no true refcount.
|
||||
return 1;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) DebugOutputBase::Release(THIS)
|
||||
{
|
||||
// This class is designed to be static so
|
||||
// there's no true refcount.
|
||||
return 0;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugOutputBase::Output(
|
||||
THIS_
|
||||
IN ULONG mask,
|
||||
IN PCWSTR text
|
||||
)
|
||||
{
|
||||
const QString msg = QString::fromUtf16(reinterpret_cast<const ushort *>(text));
|
||||
output(mask, msg);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IDebugOutputCallbacksWide *DebugOutputBase::getOutputCallback(CIDebugClient *client)
|
||||
{
|
||||
IDebugOutputCallbacksWide *rc;
|
||||
if (FAILED(client->GetOutputCallbacksWide(&rc)))
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
const char *DebugOutputBase::maskDescription(ULONG m)
|
||||
{
|
||||
switch (m) {
|
||||
case DEBUG_OUTPUT_NORMAL:
|
||||
break;
|
||||
case DEBUG_OUTPUT_ERROR:
|
||||
return "error";
|
||||
case DEBUG_OUTPUT_WARNING:
|
||||
return "warn";
|
||||
case DEBUG_OUTPUT_VERBOSE:
|
||||
return "verbose";
|
||||
case DEBUG_OUTPUT_PROMPT_REGISTERS:
|
||||
return "register";
|
||||
case DEBUG_OUTPUT_EXTENSION_WARNING:
|
||||
return "extwarn";
|
||||
case DEBUG_OUTPUT_DEBUGGEE:
|
||||
return "target";
|
||||
case DEBUG_OUTPUT_DEBUGGEE_PROMPT:
|
||||
return "input";
|
||||
case DEBUG_OUTPUT_SYMBOLS:
|
||||
return "symbol";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "misc";
|
||||
}
|
||||
|
||||
OutputRedirector::OutputRedirector(CoreEngine *engine, const DebugOutputBasePtr &o) :
|
||||
m_engine(engine),
|
||||
m_oldOutput(engine->setDebugOutput(o))
|
||||
{
|
||||
}
|
||||
|
||||
OutputRedirector::~OutputRedirector()
|
||||
{
|
||||
m_engine->setDebugOutput(m_oldOutput);
|
||||
}
|
||||
|
||||
} // namespace CdbCore
|
||||
@@ -1,115 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef DEBUGOUTPUTBASE_H
|
||||
#define DEBUGOUTPUTBASE_H
|
||||
|
||||
#include "cdbcom.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
namespace CdbCore {
|
||||
|
||||
class CoreEngine;
|
||||
|
||||
// CdbDebugOutputBase is a base class for output handlers
|
||||
// that takes care of the Active X magic and conversion to QString.
|
||||
|
||||
class DebugOutputBase : public IDebugOutputCallbacksWide
|
||||
{
|
||||
public:
|
||||
virtual ~DebugOutputBase();
|
||||
// IUnknown.
|
||||
STDMETHOD(QueryInterface)(
|
||||
THIS_
|
||||
IN REFIID InterfaceId,
|
||||
OUT PVOID* Interface
|
||||
);
|
||||
STDMETHOD_(ULONG, AddRef)(
|
||||
THIS
|
||||
);
|
||||
STDMETHOD_(ULONG, Release)(
|
||||
THIS
|
||||
);
|
||||
|
||||
// IDebugOutputCallbacks.
|
||||
STDMETHOD(Output)(
|
||||
THIS_
|
||||
IN ULONG mask,
|
||||
IN PCWSTR text
|
||||
);
|
||||
|
||||
// Helpers to retrieve the output callbacks IF
|
||||
static IDebugOutputCallbacksWide *getOutputCallback(CIDebugClient *client);
|
||||
static const char *maskDescription(ULONG m);
|
||||
|
||||
protected:
|
||||
DebugOutputBase();
|
||||
virtual void output(ULONG mask, const QString &message) = 0;
|
||||
};
|
||||
|
||||
// An output handler that adds lines to a string (to be
|
||||
// used for cases in which linebreaks occur in-between calls
|
||||
// to output).
|
||||
class StringOutputHandler : public DebugOutputBase
|
||||
{
|
||||
public:
|
||||
StringOutputHandler() {}
|
||||
QString result() const { return m_result; }
|
||||
|
||||
protected:
|
||||
virtual void output(ULONG, const QString &message) { m_result += message; }
|
||||
|
||||
private:
|
||||
QString m_result;
|
||||
};
|
||||
|
||||
// Utility class to temporarily redirect output to another handler
|
||||
// as long as in scope
|
||||
class OutputRedirector {
|
||||
Q_DISABLE_COPY(OutputRedirector)
|
||||
public:
|
||||
typedef QSharedPointer<DebugOutputBase> DebugOutputBasePtr;
|
||||
|
||||
explicit OutputRedirector(CoreEngine *engine, const DebugOutputBasePtr &o);
|
||||
~OutputRedirector();
|
||||
|
||||
private:
|
||||
CoreEngine *m_engine;
|
||||
const DebugOutputBasePtr m_oldOutput;
|
||||
};
|
||||
|
||||
} // namespace CdbCore
|
||||
|
||||
#endif // DEBUGOUTPUTBASE_H
|
||||
@@ -1,467 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "stacktracecontext.h"
|
||||
#include "symbolgroupcontext.h"
|
||||
#include "corebreakpoint.h"
|
||||
#include "coreengine.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QScopedArrayPointer>
|
||||
|
||||
enum { debug = 0 };
|
||||
|
||||
namespace CdbCore {
|
||||
|
||||
StackFrame::StackFrame() :
|
||||
line(0), address(0)
|
||||
{
|
||||
}
|
||||
|
||||
QString StackFrame::toString() const
|
||||
{
|
||||
QString rc;
|
||||
QTextStream str(&rc);
|
||||
format(str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug d, const StackFrame &f)
|
||||
{
|
||||
d.nospace() << f.toString();
|
||||
return d;
|
||||
}
|
||||
|
||||
void StackFrame::format(QTextStream &str) const
|
||||
{
|
||||
// left-pad level
|
||||
if (hasFile())
|
||||
str << QDir::toNativeSeparators(fileName) << ':' << line << " (";
|
||||
if (!module.isEmpty())
|
||||
str << module << '!';
|
||||
str << function;
|
||||
if (hasFile())
|
||||
str << ')';
|
||||
str.setIntegerBase(16);
|
||||
str << " 0x" << address;
|
||||
str.setIntegerBase(10);
|
||||
}
|
||||
|
||||
Thread::Thread(unsigned long i, unsigned long si) :
|
||||
id(i), systemId(si), dataOffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
QString Thread::toString() const
|
||||
{
|
||||
QString rc;
|
||||
QTextStream str(&rc);
|
||||
str << "Thread id " << id << " System id " << systemId
|
||||
<< " name='" << name <<"' Data at 0x";
|
||||
str.setIntegerBase(16);
|
||||
str << dataOffset;
|
||||
return rc;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug d, const Thread &t)
|
||||
{
|
||||
d.nospace() << t.toString();
|
||||
return d;
|
||||
}
|
||||
|
||||
// Check for special functions
|
||||
StackTraceContext::SpecialFunction StackTraceContext::specialFunction(const QString &module,
|
||||
const QString &function)
|
||||
{
|
||||
if (module == QLatin1String("ntdll")) {
|
||||
if (function == QLatin1String("DbgBreakPoint"))
|
||||
return BreakPointFunction;
|
||||
if (function == QLatin1String("KiFastSystemCallRet"))
|
||||
return KiFastSystemCallRet;
|
||||
if (function.startsWith("ZwWaitFor"))
|
||||
return WaitFunction;
|
||||
}
|
||||
if (module == QLatin1String("kernel32")) {
|
||||
if (function == QLatin1String("MsgWaitForMultipleObjects"))
|
||||
return WaitFunction;
|
||||
if (function.startsWith(QLatin1String("WaitFor")))
|
||||
return WaitFunction;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
StackTraceContext::StackTraceContext(const ComInterfaces *cif) :
|
||||
m_cif(cif),
|
||||
m_instructionOffset(0),
|
||||
m_lastIndex(-1)
|
||||
{
|
||||
}
|
||||
|
||||
StackTraceContext *StackTraceContext::create(const ComInterfaces *cif,
|
||||
unsigned long maxFramesIn,
|
||||
QString *errorMessage)
|
||||
{
|
||||
StackTraceContext *ctx = new StackTraceContext(cif);
|
||||
if (!ctx->init(maxFramesIn, errorMessage)) {
|
||||
delete ctx;
|
||||
return 0;
|
||||
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
StackTraceContext::~StackTraceContext()
|
||||
{
|
||||
qDeleteAll(m_frameContexts);
|
||||
}
|
||||
|
||||
// Convert the DEBUG_STACK_FRAMEs to our StackFrame structure
|
||||
StackFrame StackTraceContext::frameFromFRAME(const CdbCore::ComInterfaces &cif,
|
||||
const DEBUG_STACK_FRAME &s)
|
||||
{
|
||||
static WCHAR wszBuf[MAX_PATH];
|
||||
StackFrame frame;
|
||||
frame.address = s.InstructionOffset;
|
||||
cif.debugSymbols->GetNameByOffsetWide(frame.address, wszBuf, MAX_PATH, 0, 0);
|
||||
// Determine function and module, if available ("Qt4Core!foo").
|
||||
const QString moduleFunction = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
|
||||
const int moduleSepPos = moduleFunction.indexOf(QLatin1Char('!'));
|
||||
if (moduleSepPos == -1) {
|
||||
frame.function = moduleFunction;
|
||||
} else {
|
||||
frame.module = moduleFunction.left(moduleSepPos);
|
||||
frame.function = moduleFunction.mid(moduleSepPos + 1);
|
||||
}
|
||||
ULONG64 ul64Displacement;
|
||||
const HRESULT hr = cif.debugSymbols->GetLineByOffsetWide(frame.address, &frame.line, wszBuf, MAX_PATH, 0, &ul64Displacement);
|
||||
if (SUCCEEDED(hr)) {
|
||||
const QString rawName = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
|
||||
if (!rawName.isEmpty())
|
||||
frame.fileName = BreakPoint::normalizeFileName(rawName);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
bool StackTraceContext::init(unsigned long maxFramesIn, QString *errorMessage)
|
||||
{
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << maxFramesIn;
|
||||
|
||||
// fill the DEBUG_STACK_FRAME array
|
||||
ULONG frameCount;
|
||||
const unsigned long effectiveMaxFrames = qMin(maxFramesIn, unsigned long(StackTraceContext::maxFrames));
|
||||
const HRESULT hr = m_cif->debugControl->GetStackTrace(0, 0, 0, m_cdbFrames,
|
||||
effectiveMaxFrames,
|
||||
&frameCount);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = CdbCore::msgComFailed("GetStackTrace", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adapt group cache.
|
||||
m_frameContexts.resize(frameCount);
|
||||
qFill(m_frameContexts, static_cast<SymbolGroupContext*>(0));
|
||||
// Convert the DEBUG_STACK_FRAMEs to our StackFrame structure and populate the frames
|
||||
for (ULONG i=0; i < frameCount; ++i)
|
||||
m_frames.push_back(frameFromFRAME(*m_cif, m_cdbFrames[i]));
|
||||
m_instructionOffset = m_frames.empty() ? ULONG64(0) : m_frames.front().address;
|
||||
return true;
|
||||
}
|
||||
|
||||
int StackTraceContext::indexOf(const QString &function,
|
||||
const QString &module /* = QString() */) const
|
||||
{
|
||||
const bool noModuleMatch = module.isEmpty();
|
||||
const int count = m_frames.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (m_frames.at(i).function == function
|
||||
&& (noModuleMatch || module == m_frames.at(i).module))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString StackTraceContext::msgFrameContextFailed(int index, const StackFrame &f, const QString &why)
|
||||
{
|
||||
return QString::fromLatin1("Unable to create stack frame context #%1, %2!%3:%4 (%5): %6").
|
||||
arg(index).arg(f.module).arg(f.function).arg(f.line).arg(f.fileName, why);
|
||||
}
|
||||
|
||||
SymbolGroupContext *StackTraceContext::createSymbolGroup(const ComInterfaces &cif,
|
||||
int /* index */,
|
||||
const QString &prefix,
|
||||
CIDebugSymbolGroup *comSymbolGroup,
|
||||
QString *errorMessage)
|
||||
{
|
||||
return SymbolGroupContext::create(prefix, comSymbolGroup, cif.debugDataSpaces,
|
||||
QStringList(), errorMessage);
|
||||
}
|
||||
|
||||
SymbolGroupContext *StackTraceContext::symbolGroupContextAt(int index, QString *errorMessage)
|
||||
{
|
||||
// Create a frame on demand
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << index;
|
||||
|
||||
if (index < 0 || index >= m_frameContexts.size()) {
|
||||
*errorMessage = QString::fromLatin1("%1: Index %2 out of range %3.").
|
||||
arg(QLatin1String(Q_FUNC_INFO)).arg(index).arg(m_frameContexts.size());
|
||||
return 0;
|
||||
}
|
||||
if (m_frameContexts.at(index)) {
|
||||
// Symbol group only functions correctly if IDebugSymbols has the right scope.
|
||||
if (m_lastIndex != index) {
|
||||
if (!setScope(index, errorMessage))
|
||||
return 0;
|
||||
m_lastIndex = index;
|
||||
}
|
||||
return m_frameContexts.at(index);
|
||||
}
|
||||
CIDebugSymbolGroup *comSymbolGroup = createCOM_SymbolGroup(index, errorMessage);
|
||||
if (!comSymbolGroup) {
|
||||
*errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage);
|
||||
return 0;
|
||||
}
|
||||
SymbolGroupContext *sc = createSymbolGroup(*m_cif, index, QLatin1String("local"),
|
||||
comSymbolGroup, errorMessage);
|
||||
if (!sc) {
|
||||
*errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage);
|
||||
return 0;
|
||||
}
|
||||
m_frameContexts[index] = sc;
|
||||
return sc;
|
||||
}
|
||||
|
||||
bool StackTraceContext::setScope(int index, QString *errorMessage)
|
||||
{
|
||||
if (debug)
|
||||
qDebug() << "setScope" << index;
|
||||
const HRESULT hr = m_cif->debugSymbols->SetScope(0, m_cdbFrames + index, NULL, 0);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = QString::fromLatin1("Cannot set scope %1: %2").
|
||||
arg(index).arg(CdbCore::msgComFailed("SetScope", hr));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CIDebugSymbolGroup *StackTraceContext::createCOM_SymbolGroup(int index, QString *errorMessage)
|
||||
{
|
||||
CIDebugSymbolGroup *sg = 0;
|
||||
HRESULT hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &sg);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = CdbCore::msgComFailed("GetScopeSymbolGroup", hr);
|
||||
return 0;
|
||||
}
|
||||
// Set debugSymbols's scope.
|
||||
if (!setScope(index, errorMessage)) {
|
||||
sg->Release();
|
||||
return 0;
|
||||
}
|
||||
// refresh with current frame
|
||||
hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, sg, &sg);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = CdbCore::msgComFailed("GetScopeSymbolGroup", hr);
|
||||
sg->Release();
|
||||
return 0;
|
||||
}
|
||||
m_lastIndex = index;
|
||||
return sg;
|
||||
}
|
||||
|
||||
QString StackTraceContext::toString() const
|
||||
{
|
||||
QString rc;
|
||||
QTextStream str(&rc);
|
||||
format(str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void StackTraceContext::format(QTextStream &str) const
|
||||
{
|
||||
const int count = m_frames.count();
|
||||
const int defaultFieldWidth = str.fieldWidth();
|
||||
const QTextStream::FieldAlignment defaultAlignment = str.fieldAlignment();
|
||||
for (int f = 0; f < count; f++) {
|
||||
// left-pad level
|
||||
str << qSetFieldWidth(6) << left << f;
|
||||
str.setFieldWidth(defaultFieldWidth);
|
||||
str.setFieldAlignment(defaultAlignment);
|
||||
m_frames.at(f).format(str);
|
||||
str << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// Thread state helper
|
||||
static inline QString msgGetThreadStateFailed(unsigned long threadId, const QString &why)
|
||||
{
|
||||
return QString::fromLatin1("Unable to determine the state of thread %1: %2").arg(threadId).arg(why);
|
||||
}
|
||||
|
||||
// Determine information about thread. This changes the
|
||||
// current thread to thread->id.
|
||||
bool StackTraceContext::getStoppedThreadState(const CdbCore::ComInterfaces &cif,
|
||||
unsigned long id,
|
||||
StackFrame *topFrame,
|
||||
QString *errorMessage)
|
||||
{
|
||||
enum { MaxFrames = 2 };
|
||||
ULONG currentThread;
|
||||
HRESULT hr = cif.debugSystemObjects->GetCurrentThreadId(¤tThread);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgGetThreadStateFailed(id, CdbCore::msgComFailed("GetCurrentThreadId", hr));
|
||||
return false;
|
||||
}
|
||||
if (currentThread != id) {
|
||||
hr = cif.debugSystemObjects->SetCurrentThreadId(id);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgGetThreadStateFailed(id, CdbCore::msgComFailed("SetCurrentThreadId", hr));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ULONG frameCount;
|
||||
// Ignore the top frame if it is "ntdll!KiFastSystemCallRet", which is
|
||||
// not interesting for display.
|
||||
DEBUG_STACK_FRAME frames[MaxFrames];
|
||||
hr = cif.debugControl->GetStackTrace(0, 0, 0, frames, MaxFrames, &frameCount);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgGetThreadStateFailed(id, CdbCore::msgComFailed("GetStackTrace", hr));
|
||||
return false;
|
||||
}
|
||||
// Ignore the top frame if it is "ntdll!KiFastSystemCallRet", which is
|
||||
// not interesting for display.
|
||||
*topFrame = frameFromFRAME(cif, frames[0]);
|
||||
if (frameCount > 1
|
||||
&& StackTraceContext::specialFunction(topFrame->module, topFrame->function) == KiFastSystemCallRet)
|
||||
*topFrame = frameFromFRAME(cif, frames[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline QString msgGetThreadsFailed(const QString &why)
|
||||
{
|
||||
return QString::fromLatin1("Unable to determine the thread information: %1").arg(why);
|
||||
}
|
||||
|
||||
bool StackTraceContext::getThreadList(const CdbCore::ComInterfaces &cif,
|
||||
QVector<Thread> *threads,
|
||||
ULONG *currentThreadId,
|
||||
QString *errorMessage)
|
||||
{
|
||||
threads->clear();
|
||||
ULONG threadCount;
|
||||
*currentThreadId = 0;
|
||||
// Get count
|
||||
HRESULT hr= cif.debugSystemObjects->GetNumberThreads(&threadCount);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetNumberThreads", hr));
|
||||
return false;
|
||||
}
|
||||
// Get index of current
|
||||
if (!threadCount)
|
||||
return true;
|
||||
hr = cif.debugSystemObjects->GetCurrentThreadId(currentThreadId);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetCurrentThreadId", hr));
|
||||
return false;
|
||||
}
|
||||
// Get Identifiers
|
||||
threads->reserve(threadCount);
|
||||
QScopedArrayPointer<ULONG> ids(new ULONG[threadCount]);
|
||||
QScopedArrayPointer<ULONG> systemIds(new ULONG[threadCount]);
|
||||
hr = cif.debugSystemObjects->GetThreadIdsByIndex(0, threadCount, ids.data(), systemIds.data());
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetThreadIdsByIndex", hr));
|
||||
return false;
|
||||
}
|
||||
// Create entries
|
||||
static WCHAR name[256];
|
||||
for (ULONG i= 0; i < threadCount ; i++) {
|
||||
const ULONG id = ids[i];
|
||||
Thread thread(id, systemIds[i]);
|
||||
if (ids[i] == *currentThreadId) { // More info for current
|
||||
ULONG64 offset;
|
||||
if (SUCCEEDED(cif.debugSystemObjects->GetCurrentThreadDataOffset(&offset)))
|
||||
thread.dataOffset = offset;
|
||||
}
|
||||
// Name
|
||||
ULONG bytesReceived = 0;
|
||||
hr = cif.debugAdvanced->GetSystemObjectInformation(DEBUG_SYSOBJINFO_THREAD_NAME_WIDE,
|
||||
0, id, name,
|
||||
sizeof(name), &bytesReceived);
|
||||
if (SUCCEEDED(hr) && bytesReceived)
|
||||
thread.name = QString::fromWCharArray(name);
|
||||
threads->push_back(thread);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StackTraceContext::getStoppedThreadFrames(const CdbCore::ComInterfaces &cif,
|
||||
ULONG currentThreadId,
|
||||
const QVector<Thread> &threads,
|
||||
QVector<StackFrame> *frames,
|
||||
QString *errorMessage)
|
||||
{
|
||||
frames->clear();
|
||||
if (threads.isEmpty())
|
||||
return true;
|
||||
frames->reserve(threads.size());
|
||||
|
||||
const int threadCount = threads.size();
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
StackFrame frame;
|
||||
if (!getStoppedThreadState(cif, threads.at(i).id, &frame, errorMessage)) {
|
||||
qWarning("%s\n", qPrintable(*errorMessage));
|
||||
errorMessage->clear();
|
||||
}
|
||||
frames->append(frame);
|
||||
}
|
||||
// Restore thread id
|
||||
if (threads.back().id != currentThreadId) {
|
||||
const HRESULT hr = cif.debugSystemObjects->SetCurrentThreadId(currentThreadId);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("SetCurrentThreadId", hr));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug d, const StackTraceContext &t)
|
||||
{
|
||||
d.nospace() << t.toString();
|
||||
return d;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CORESTACKTRACECONTEXT_H
|
||||
#define CORESTACKTRACECONTEXT_H
|
||||
|
||||
#include "cdbcom.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QMap>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextStream;
|
||||
class QDebug;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace CdbCore {
|
||||
|
||||
struct ComInterfaces;
|
||||
class SymbolGroupContext;
|
||||
|
||||
|
||||
struct StackFrame {
|
||||
StackFrame();
|
||||
|
||||
bool hasFile() const { return !fileName.isEmpty(); }
|
||||
void format(QTextStream &) const;
|
||||
|
||||
QString toString() const;
|
||||
|
||||
QString module;
|
||||
QString function;
|
||||
QString fileName;
|
||||
ULONG line;
|
||||
ULONG64 address;
|
||||
};
|
||||
|
||||
struct Thread {
|
||||
explicit Thread(unsigned long id = 0, unsigned long sysId = 0);
|
||||
QString toString() const;
|
||||
|
||||
unsigned long id;
|
||||
unsigned long systemId;
|
||||
quint64 dataOffset; // Only for current.
|
||||
QString name;
|
||||
};
|
||||
|
||||
inline bool operator<(const Thread &t1, const Thread &t2) { return t1.id < t2.id; }
|
||||
|
||||
QDebug operator<<(QDebug d, const StackFrame &);
|
||||
QDebug operator<<(QDebug d, const Thread &);
|
||||
|
||||
/* Context representing a break point stack consisting of several frames.
|
||||
* Maintains an on-demand constructed list of SymbolGroupContext
|
||||
* containining the local variables of the stack. */
|
||||
|
||||
class StackTraceContext
|
||||
{
|
||||
Q_DISABLE_COPY(StackTraceContext)
|
||||
|
||||
protected:
|
||||
explicit StackTraceContext(const ComInterfaces *cif);
|
||||
bool init(unsigned long maxFramesIn, QString *errorMessage);
|
||||
|
||||
public:
|
||||
// Utilities to check for special functions
|
||||
enum SpecialFunction {
|
||||
BreakPointFunction,
|
||||
WaitFunction,
|
||||
KiFastSystemCallRet,
|
||||
None
|
||||
};
|
||||
static SpecialFunction specialFunction(const QString &module, const QString &function);
|
||||
|
||||
enum { maxFrames = 100 };
|
||||
|
||||
~StackTraceContext();
|
||||
static StackTraceContext *create(const ComInterfaces *cif,
|
||||
unsigned long maxFramesIn,
|
||||
QString *errorMessage);
|
||||
|
||||
// Search for function. Empty module means "don't match on module"
|
||||
int indexOf(const QString &function, const QString &module = QString()) const;
|
||||
|
||||
// Top-Level instruction offset for disassembler
|
||||
ULONG64 instructionOffset() const { return m_instructionOffset; }
|
||||
int frameCount() const { return m_frames.size(); }
|
||||
|
||||
SymbolGroupContext *symbolGroupContextAt(int index, QString *errorMessage);
|
||||
const StackFrame stackFrameAt(int index) const { return m_frames.at(index); }
|
||||
|
||||
// Format for logging
|
||||
void format(QTextStream &str) const;
|
||||
QString toString() const;
|
||||
|
||||
// Thread helpers: Retrieve a list of thread ids. Also works when running.
|
||||
static bool getThreadList(const CdbCore::ComInterfaces &cif,
|
||||
QVector<Thread> *threads,
|
||||
ULONG *currentThreadId,
|
||||
QString *errorMessage);
|
||||
|
||||
// Retrieve detailed information about a threads in stopped state.
|
||||
// Potentially changes current thread id.
|
||||
static inline bool getStoppedThreadState(const CdbCore::ComInterfaces &cif,
|
||||
unsigned long id,
|
||||
StackFrame *topFrame,
|
||||
QString *errorMessage);
|
||||
|
||||
// Get the stack traces for threads in stopped state (only, fails when running).
|
||||
// Potentially changes and restores current thread.
|
||||
static bool getStoppedThreadFrames(const CdbCore::ComInterfaces &cif,
|
||||
ULONG currentThreadId,
|
||||
const QVector<Thread> &threads,
|
||||
QVector<StackFrame> *frames,
|
||||
QString *errorMessage);
|
||||
|
||||
protected:
|
||||
virtual SymbolGroupContext *createSymbolGroup(const ComInterfaces &cif,
|
||||
int index,
|
||||
const QString &prefix,
|
||||
CIDebugSymbolGroup *comSymbolGroup,
|
||||
QString *errorMessage);
|
||||
|
||||
static QString msgFrameContextFailed(int index, const StackFrame &f, const QString &why);
|
||||
|
||||
private:
|
||||
bool setScope(int index, QString *errorMessage);
|
||||
CIDebugSymbolGroup *createCOM_SymbolGroup(int index, QString *errorMessage);
|
||||
inline static StackFrame frameFromFRAME(const CdbCore::ComInterfaces &cif,
|
||||
const DEBUG_STACK_FRAME &s);
|
||||
|
||||
// const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||
const ComInterfaces *m_cif;
|
||||
|
||||
DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
|
||||
QVector <SymbolGroupContext*> m_frameContexts;
|
||||
QVector<StackFrame> m_frames;
|
||||
ULONG64 m_instructionOffset;
|
||||
int m_lastIndex;
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug d, const StackTraceContext &);
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
#endif // CORESTACKTRACECONTEXT_H
|
||||
@@ -1,851 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "symbolgroupcontext.h"
|
||||
#include "coreengine.h"
|
||||
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QRegExp>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
enum { debug = 0 };
|
||||
enum { debugInternalDumpers = 0 };
|
||||
|
||||
// name separator for shadowed variables
|
||||
static const char iNameShadowDelimiter = '#';
|
||||
|
||||
static inline QString msgSymbolNotFound(const QString &s)
|
||||
{
|
||||
return QString::fromLatin1("The symbol '%1' could not be found.").arg(s);
|
||||
}
|
||||
|
||||
static inline QString msgOutOfScope()
|
||||
{
|
||||
return QCoreApplication::translate("SymbolGroup", "Out of scope");
|
||||
}
|
||||
|
||||
static inline bool isTopLevelSymbol(const DEBUG_SYMBOL_PARAMETERS &p)
|
||||
{
|
||||
return p.ParentSymbol == DEBUG_ANY_ID;
|
||||
}
|
||||
|
||||
static inline void debugSymbolFlags(unsigned long f, QTextStream &str)
|
||||
{
|
||||
if (f & DEBUG_SYMBOL_EXPANDED)
|
||||
str << "DEBUG_SYMBOL_EXPANDED";
|
||||
if (f & DEBUG_SYMBOL_READ_ONLY)
|
||||
str << "|DEBUG_SYMBOL_READ_ONLY";
|
||||
if (f & DEBUG_SYMBOL_IS_ARRAY)
|
||||
str << "|DEBUG_SYMBOL_IS_ARRAY";
|
||||
if (f & DEBUG_SYMBOL_IS_FLOAT)
|
||||
str << "|DEBUG_SYMBOL_IS_FLOAT";
|
||||
if (f & DEBUG_SYMBOL_IS_ARGUMENT)
|
||||
str << "|DEBUG_SYMBOL_IS_ARGUMENT";
|
||||
if (f & DEBUG_SYMBOL_IS_LOCAL)
|
||||
str << "|DEBUG_SYMBOL_IS_LOCAL";
|
||||
}
|
||||
|
||||
QTextStream &operator<<(QTextStream &str, const DEBUG_SYMBOL_PARAMETERS &p)
|
||||
{
|
||||
str << " Type=" << p.TypeId << " parent=";
|
||||
if (isTopLevelSymbol(p)) {
|
||||
str << "<ROOT>";
|
||||
} else {
|
||||
str << p.ParentSymbol;
|
||||
}
|
||||
str << " Subs=" << p.SubElements << " flags=" << p.Flags << '/';
|
||||
debugSymbolFlags(p.Flags, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
static inline ULONG64 symbolOffset(CIDebugSymbolGroup *sg, unsigned long index)
|
||||
{
|
||||
ULONG64 rc = 0;
|
||||
if (FAILED(sg->GetSymbolOffset(index, &rc)))
|
||||
rc = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
// A helper function to extract a string value from a member function of
|
||||
// IDebugSymbolGroup2 taking the symbol index and a character buffer.
|
||||
// Pass in the the member function as '&IDebugSymbolGroup2::GetSymbolNameWide'
|
||||
|
||||
typedef HRESULT (__stdcall IDebugSymbolGroup2::*WideStringRetrievalFunction)(ULONG, PWSTR, ULONG, PULONG);
|
||||
|
||||
static inline QString getSymbolString(IDebugSymbolGroup2 *sg,
|
||||
WideStringRetrievalFunction wsf,
|
||||
unsigned long index)
|
||||
{
|
||||
// Template type names can get quite long....
|
||||
enum { BufSize = 1024 };
|
||||
static WCHAR nameBuffer[BufSize + 1];
|
||||
// Name
|
||||
ULONG nameLength;
|
||||
const HRESULT hr = (sg->*wsf)(index, nameBuffer, BufSize, &nameLength);
|
||||
if (SUCCEEDED(hr)) {
|
||||
nameBuffer[qMin(nameLength, ULONG(BufSize))] = 0;
|
||||
return QString::fromUtf16(reinterpret_cast<const ushort *>(nameBuffer));
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
namespace CdbCore {
|
||||
|
||||
static inline SymbolGroupContext::SymbolState getSymbolState(const DEBUG_SYMBOL_PARAMETERS &p)
|
||||
{
|
||||
if (p.SubElements == 0u)
|
||||
return SymbolGroupContext::LeafSymbol;
|
||||
return (p.Flags & DEBUG_SYMBOL_EXPANDED) ?
|
||||
SymbolGroupContext::ExpandedSymbol :
|
||||
SymbolGroupContext::CollapsedSymbol;
|
||||
}
|
||||
|
||||
SymbolGroupContext::SymbolGroupContext(const QString &prefix,
|
||||
CIDebugSymbolGroup *symbolGroup,
|
||||
CIDebugDataSpaces *dataSpaces,
|
||||
const QStringList &uninitializedVariables) :
|
||||
m_prefix(prefix),
|
||||
m_nameDelimiter(QLatin1Char('.')),
|
||||
m_uninitializedVariables(uninitializedVariables.toSet()),
|
||||
m_symbolGroup(symbolGroup),
|
||||
m_dataSpaces(dataSpaces),
|
||||
m_unnamedSymbolNumber(1),
|
||||
m_shadowedNameFormat(QLatin1String("%1#%2"))
|
||||
{
|
||||
}
|
||||
|
||||
SymbolGroupContext::~SymbolGroupContext()
|
||||
{
|
||||
m_symbolGroup->Release();
|
||||
}
|
||||
|
||||
SymbolGroupContext *SymbolGroupContext::create(const QString &prefix,
|
||||
CIDebugSymbolGroup *symbolGroup,
|
||||
CIDebugDataSpaces *dataSpaces,
|
||||
const QStringList &uninitializedVariables,
|
||||
QString *errorMessage)
|
||||
{
|
||||
SymbolGroupContext *rc = new SymbolGroupContext(prefix, symbolGroup, dataSpaces, uninitializedVariables);
|
||||
if (!rc->init(errorMessage)) {
|
||||
delete rc;
|
||||
return 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool SymbolGroupContext::init(QString *errorMessage)
|
||||
{
|
||||
// retrieve the root symbols
|
||||
ULONG count;
|
||||
HRESULT hr = m_symbolGroup->GetNumberSymbols(&count);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = CdbCore::msgComFailed("GetNumberSymbols", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count) {
|
||||
m_symbolParameters.reserve(3u * count);
|
||||
m_symbolParameters.resize(count);
|
||||
|
||||
hr = m_symbolGroup->GetSymbolParameters(0, count, symbolParameters());
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = QString::fromLatin1("In %1: %2 (%3 symbols)").arg(QLatin1String(Q_FUNC_INFO),
|
||||
CdbCore::msgComFailed("GetSymbolParameters", hr)).arg(count);
|
||||
return false;
|
||||
}
|
||||
populateINameIndexMap(m_prefix, DEBUG_ANY_ID, count);
|
||||
}
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << '\n'<< debugToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
QString SymbolGroupContext::shadowedNameFormat() const
|
||||
{
|
||||
return m_shadowedNameFormat;
|
||||
}
|
||||
|
||||
void SymbolGroupContext::setShadowedNameFormat(const QString &f)
|
||||
{
|
||||
m_shadowedNameFormat = f;
|
||||
}
|
||||
|
||||
/* Make the entries for iname->index mapping. We might encounter
|
||||
* already expanded subitems when doing it for top-level ('this'-pointers),
|
||||
* recurse in that case, (skip over expanded children).
|
||||
* Loop backwards to detect shadowed variables in the order the
|
||||
/* debugger expects them:
|
||||
\code
|
||||
int x; // Occurrence (1), should be reported as "x <shadowed 1>"
|
||||
if (true) {
|
||||
int x = 5; (2) // Occurrence (2), should be reported as "x"
|
||||
}
|
||||
\endcode
|
||||
* The order in the symbol group is (1),(2). Give them an iname of
|
||||
* <root>#<shadowed-nr>, which will be split apart for display. */
|
||||
|
||||
void SymbolGroupContext::populateINameIndexMap(const QString &prefix, unsigned long parentId,
|
||||
unsigned long end)
|
||||
{
|
||||
const QString symbolPrefix = prefix + m_nameDelimiter;
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << '\n'<< symbolPrefix << parentId << end;
|
||||
for (unsigned long i = end - 1; ; i--) {
|
||||
const DEBUG_SYMBOL_PARAMETERS &p = m_symbolParameters.at(i);
|
||||
if (parentId == p.ParentSymbol) {
|
||||
// "__formal" occurs when someone writes "void foo(int /* x */)..."
|
||||
static const QString unnamedFormalParameter = QLatin1String("__formal");
|
||||
QString symbolName = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolNameWide, i);
|
||||
if (symbolName == unnamedFormalParameter) {
|
||||
symbolName = QLatin1String("<unnamed");
|
||||
symbolName += QString::number(m_unnamedSymbolNumber++);
|
||||
symbolName += QLatin1Char('>');
|
||||
} else {
|
||||
// Trigger numeric sorting for arrays "local.[22]" -> "local.22"
|
||||
if (symbolName.startsWith(QLatin1Char('[')) && symbolName.endsWith(QLatin1Char(']'))) {
|
||||
symbolName.truncate(symbolName.size() - 1);
|
||||
symbolName.remove(0, 1);
|
||||
}
|
||||
}
|
||||
// Find a unique name in case the variable is shadowed by
|
||||
// an existing one
|
||||
const QString namePrefix = symbolPrefix + symbolName;
|
||||
QString name = namePrefix;
|
||||
for (int n = 1; m_inameIndexMap.contains(name); n++) {
|
||||
name.truncate(namePrefix.size());
|
||||
name += QLatin1Char(iNameShadowDelimiter);
|
||||
name += QString::number(n);
|
||||
}
|
||||
m_inameIndexMap.insert(name, i);
|
||||
if (getSymbolState(p) == ExpandedSymbol)
|
||||
populateINameIndexMap(name, i, i + 1 + p.SubElements);
|
||||
}
|
||||
if (i == 0 || i == parentId)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QString SymbolGroupContext::toString()
|
||||
{
|
||||
QString rc;
|
||||
QTextStream str(&rc);
|
||||
const unsigned long count = m_symbolParameters.size();
|
||||
QString iname;
|
||||
QString name;
|
||||
ULONG64 addr;
|
||||
ULONG typeId;
|
||||
QString typeName;
|
||||
QString value;
|
||||
|
||||
|
||||
for (unsigned long i = 0; i < count; i++) {
|
||||
const unsigned rc = dumpValue(i, &iname, &name, &addr,
|
||||
&typeId, &typeName, &value);
|
||||
str << iname << ' ' << name << ' ' << typeName << " (" << typeId
|
||||
<< ") '" << value;
|
||||
str.setIntegerBase(16);
|
||||
str << "' 0x" << addr << " flags: 0x" <<rc << '\n';
|
||||
str.setIntegerBase(10);
|
||||
} // for
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
QString SymbolGroupContext::debugToString(bool verbose) const
|
||||
{
|
||||
QString rc;
|
||||
QTextStream str(&rc);
|
||||
const int count = m_symbolParameters.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
str << i << ' ';
|
||||
const DEBUG_SYMBOL_PARAMETERS &p = m_symbolParameters.at(i);
|
||||
if (!isTopLevelSymbol(p))
|
||||
str << " ";
|
||||
str << getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolNameWide, i);
|
||||
if (p.Flags & DEBUG_SYMBOL_IS_LOCAL)
|
||||
str << " '" << getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolTypeNameWide, i) << '\'';
|
||||
str << " Address: " << symbolOffset(m_symbolGroup, i);
|
||||
if (verbose)
|
||||
str << " '" << getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, i) << '\'';
|
||||
str << p << '\n';
|
||||
}
|
||||
if (verbose) {
|
||||
str << "NameIndexMap\n";
|
||||
NameIndexMap::const_iterator ncend = m_inameIndexMap.constEnd();
|
||||
for (NameIndexMap::const_iterator it = m_inameIndexMap.constBegin() ; it != ncend; ++it)
|
||||
str << it.key() << ' ' << it.value() << '\n';
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
SymbolGroupContext::SymbolState SymbolGroupContext::symbolState(unsigned long index) const
|
||||
{
|
||||
return getSymbolState(m_symbolParameters.at(index));
|
||||
}
|
||||
|
||||
SymbolGroupContext::SymbolState SymbolGroupContext::symbolState(const QString &prefix) const
|
||||
{
|
||||
if (prefix == m_prefix) // root
|
||||
return ExpandedSymbol;
|
||||
unsigned long index;
|
||||
if (!lookupPrefix(prefix, &index)) {
|
||||
qWarning("WARNING %s: %s\n", Q_FUNC_INFO, qPrintable(msgSymbolNotFound(prefix)));
|
||||
return LeafSymbol;
|
||||
}
|
||||
return symbolState(index);
|
||||
}
|
||||
|
||||
// Find index of a prefix
|
||||
bool SymbolGroupContext::lookupPrefix(const QString &prefix, unsigned long *index) const
|
||||
{
|
||||
*index = 0;
|
||||
const NameIndexMap::const_iterator it = m_inameIndexMap.constFind(prefix);
|
||||
if (it == m_inameIndexMap.constEnd())
|
||||
return false;
|
||||
*index = it.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Retrieve children and get the position. */
|
||||
bool SymbolGroupContext::getChildSymbolsPosition(const QString &prefix,
|
||||
unsigned long *start,
|
||||
unsigned long *parentId,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << '\n'<< prefix;
|
||||
|
||||
*start = *parentId = 0;
|
||||
// Root item?
|
||||
if (prefix == m_prefix) {
|
||||
*start = 0;
|
||||
*parentId = DEBUG_ANY_ID;
|
||||
if (debug)
|
||||
qDebug() << '<' << prefix << "at" << *start;
|
||||
return true;
|
||||
}
|
||||
// Get parent index, make sure it is expanded
|
||||
NameIndexMap::const_iterator nit = m_inameIndexMap.constFind(prefix);
|
||||
if (nit == m_inameIndexMap.constEnd()) {
|
||||
*errorMessage = QString::fromLatin1("'%1' not found.").arg(prefix);
|
||||
return false;
|
||||
}
|
||||
*parentId = nit.value();
|
||||
*start = nit.value() + 1;
|
||||
if (!expandSymbol(prefix, *parentId, errorMessage))
|
||||
return false;
|
||||
if (debug)
|
||||
qDebug() << '<' << prefix << "at" << *start;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline QString msgExpandFailed(const QString &prefix, unsigned long index, const QString &why)
|
||||
{
|
||||
return QString::fromLatin1("Unable to expand '%1' %2: %3").arg(prefix).arg(index).arg(why);
|
||||
}
|
||||
|
||||
bool SymbolGroupContext::expandSymbol(unsigned long index, QString *errorMessage)
|
||||
{
|
||||
return expandSymbol(m_inameIndexMap.key(index), index, errorMessage);
|
||||
}
|
||||
|
||||
// Expand a symbol using the symbol group interface.
|
||||
bool SymbolGroupContext::expandSymbol(const QString &prefix, unsigned long index, QString *errorMessage)
|
||||
{
|
||||
if (debug)
|
||||
qDebug() << '>' << Q_FUNC_INFO << '\n' << prefix << index;
|
||||
|
||||
if (index >= unsigned(m_symbolParameters.size())) {
|
||||
*errorMessage = QString::fromLatin1("Index %1 (%2) out of range 0..%3.").
|
||||
arg(index).arg(prefix).arg(m_symbolParameters.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (symbolState(index)) {
|
||||
case LeafSymbol:
|
||||
*errorMessage = QString::fromLatin1("Attempt to expand leaf node '%1' %2!").arg(prefix).arg(index);
|
||||
return false;
|
||||
case ExpandedSymbol:
|
||||
return true;
|
||||
case CollapsedSymbol:
|
||||
break;
|
||||
}
|
||||
|
||||
HRESULT hr = m_symbolGroup->ExpandSymbol(index, TRUE);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgExpandFailed(prefix, index, CdbCore::msgComFailed("ExpandSymbol", hr));
|
||||
return false;
|
||||
}
|
||||
// Hopefully, this will never fail, else data structure will be foobar.
|
||||
const ULONG oldSize = m_symbolParameters.size();
|
||||
ULONG newSize;
|
||||
hr = m_symbolGroup->GetNumberSymbols(&newSize);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgExpandFailed(prefix, index, CdbCore::msgComFailed("GetNumberSymbols", hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve the new parameter structs which will be inserted
|
||||
// after the parents, offsetting consecutive indexes.
|
||||
m_symbolParameters.resize(newSize);
|
||||
|
||||
hr = m_symbolGroup->GetSymbolParameters(0, newSize, symbolParameters());
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgExpandFailed(prefix, index, CdbCore::msgComFailed("GetSymbolParameters", hr));
|
||||
return false;
|
||||
}
|
||||
// The new symbols are inserted after the parent symbol.
|
||||
// We need to correct the following values in the name->index map
|
||||
const unsigned long newSymbolCount = newSize - oldSize;
|
||||
const NameIndexMap::iterator nend = m_inameIndexMap.end();
|
||||
for (NameIndexMap::iterator it = m_inameIndexMap.begin(); it != nend; ++it)
|
||||
if (it.value() > index)
|
||||
it.value() += newSymbolCount;
|
||||
// insert the new symbols
|
||||
populateINameIndexMap(prefix, index, index + 1 + newSymbolCount);
|
||||
if (debug > 1)
|
||||
qDebug() << '<' << Q_FUNC_INFO << '\n' << prefix << index << '\n' << toString();
|
||||
return true;
|
||||
}
|
||||
|
||||
void SymbolGroupContext::clear()
|
||||
{
|
||||
m_symbolParameters.clear();
|
||||
m_inameIndexMap.clear();
|
||||
}
|
||||
|
||||
QString SymbolGroupContext::symbolINameAt(unsigned long index) const
|
||||
{
|
||||
return m_inameIndexMap.key(index);
|
||||
}
|
||||
|
||||
// Return hexadecimal pointer value from a CDB pointer value
|
||||
// which look like "0x000032a" or "0x00000000`0250124a" or
|
||||
// "0x1`0250124a" on 64-bit systems.
|
||||
bool SymbolGroupContext::getUnsignedHexValue(QString stringValue, quint64 *value,
|
||||
int *endPos /* = 0 */)
|
||||
{
|
||||
if (endPos)
|
||||
*endPos = -1;
|
||||
*value = 0;
|
||||
if (!stringValue.startsWith(QLatin1String("0x")))
|
||||
return false;
|
||||
// Chop off character values (0x76 'a') and return right end position
|
||||
const int blankPos = stringValue.indexOf(QLatin1Char(' '));
|
||||
if (endPos)
|
||||
*endPos = blankPos != -1 ? blankPos : stringValue.size();
|
||||
if (blankPos != -1)
|
||||
stringValue.truncate(blankPos);
|
||||
stringValue.remove(0, 2); // Remove '0x', as checked above
|
||||
// Remove 64bit separator
|
||||
if (stringValue.size() > 9) {
|
||||
if (stringValue.at(8) == QLatin1Char('`'))
|
||||
stringValue.remove(8, 1);
|
||||
}
|
||||
bool ok;
|
||||
*value = stringValue.toULongLong(&ok, 16);
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Return the format specification of a '0x..', '0n..'
|
||||
// integer specification or '0' if there is none.
|
||||
inline char intFormatSpecification(const QString &stringValue)
|
||||
{
|
||||
if (stringValue.size() > 2) {
|
||||
const QChar format = stringValue.at(1);
|
||||
if (!format.isDigit())
|
||||
return format.toLatin1();
|
||||
}
|
||||
return char(0);
|
||||
}
|
||||
|
||||
QVariant SymbolGroupContext::getIntValue(const QString &stringValue)
|
||||
{
|
||||
// Is this a "0x<hex'hex>", "0n<decimal>" or something
|
||||
switch (intFormatSpecification(stringValue)) {
|
||||
case 'x': { // Hex unsigned
|
||||
quint64 uvalue;
|
||||
if (SymbolGroupContext::getUnsignedHexValue(stringValue, &uvalue))
|
||||
return QVariant(uvalue);
|
||||
}
|
||||
break;
|
||||
case '\0': // Decimal or none
|
||||
case 'n': {
|
||||
qint64 nvalue;
|
||||
if (SymbolGroupContext::getDecimalIntValue(stringValue, &nvalue))
|
||||
return QVariant(nvalue);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
qWarning("CDB: Integer conversion failed for '%s'.", qPrintable(stringValue));
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
// check for "0x000", "0x000 class X" or its 64-bit equivalents.
|
||||
bool SymbolGroupContext::isNullPointer(const QString &type , QString valueS)
|
||||
{
|
||||
if (!type.endsWith(QLatin1String(" *")))
|
||||
return false;
|
||||
const int blankPos = valueS.indexOf(QLatin1Char(' '));
|
||||
if (blankPos != -1)
|
||||
valueS.truncate(blankPos);
|
||||
quint64 value;
|
||||
return SymbolGroupContext::getUnsignedHexValue(valueS, &value) && value == 0u;
|
||||
}
|
||||
|
||||
// Fix a symbol group value. It is set to the class type for
|
||||
// expandable classes (type="class std::foo<..>[*]",
|
||||
// value="std::foo<...>[*]". This is not desired
|
||||
// as it widens the value column for complex std::template types.
|
||||
// Remove the inner template type.
|
||||
|
||||
QString SymbolGroupContext::removeInnerTemplateType(QString value)
|
||||
{
|
||||
const int firstBracketPos = value.indexOf(QLatin1Char('<'));
|
||||
const int lastBracketPos = firstBracketPos != -1 ? value.lastIndexOf(QLatin1Char('>')) : -1;
|
||||
if (lastBracketPos != -1)
|
||||
value.replace(firstBracketPos + 1, lastBracketPos - firstBracketPos -1, QLatin1String("..."));
|
||||
return value;
|
||||
}
|
||||
|
||||
QString SymbolGroupContext::formatShadowedName(const QString &name, int n) const
|
||||
{
|
||||
return n > 0 ? m_shadowedNameFormat.arg(name).arg(n) : name;
|
||||
}
|
||||
|
||||
unsigned SymbolGroupContext::dumpValueRaw(unsigned long index,
|
||||
QString *inameIn,
|
||||
QString *nameIn,
|
||||
ULONG64 *addrIn,
|
||||
ULONG *typeIdIn,
|
||||
QString *typeNameIn,
|
||||
QString *valueIn) const
|
||||
{
|
||||
unsigned rc = 0;
|
||||
const QString iname = symbolINameAt(index);
|
||||
*inameIn = iname;
|
||||
*addrIn = symbolOffset(m_symbolGroup, index);
|
||||
// Determine name from iname and format shadowed variables correctly
|
||||
// as "<shadowed X>, see populateINameIndexMap() (from "name#1").
|
||||
const int lastDelimiterPos = iname.lastIndexOf(m_nameDelimiter);
|
||||
QString name = lastDelimiterPos == -1 ? iname : iname.mid(lastDelimiterPos + 1);
|
||||
int shadowedNumber = 0;
|
||||
const int shadowedPos = name.lastIndexOf(QLatin1Char(iNameShadowDelimiter));
|
||||
if (shadowedPos != -1) {
|
||||
shadowedNumber = name.mid(shadowedPos + 1).toInt();
|
||||
name.truncate(shadowedPos);
|
||||
}
|
||||
// For class hierarchies, we get sometimes complicated std::template types here.
|
||||
// (std::map extends std::tree<>... Remove them for display only.
|
||||
*nameIn = formatShadowedName(removeInnerTemplateType(name), shadowedNumber);
|
||||
*typeNameIn = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolTypeNameWide, index);
|
||||
// Check for uninitialized variables at level 0 only.
|
||||
const DEBUG_SYMBOL_PARAMETERS &p = m_symbolParameters.at(index);
|
||||
*typeIdIn = p.TypeId;
|
||||
if (p.ParentSymbol == DEBUG_ANY_ID) {
|
||||
const QString fullShadowedName = formatShadowedName(name, shadowedNumber);
|
||||
if (m_uninitializedVariables.contains(fullShadowedName)) {
|
||||
rc |= OutOfScope;
|
||||
valueIn->clear();
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
// In scope: Figure out value
|
||||
*valueIn = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, index);
|
||||
// Figure out children. The SubElement is only a guess unless the symbol,
|
||||
// is expanded, so, we leave this as a guess for later updates.
|
||||
// If the symbol has children (expanded or not), we leave the 'Children' flag
|
||||
// in 'needed' state. Suppress 0-pointers right ("0x000 class X")
|
||||
// here as they only lead to children with memory access errors.
|
||||
if (p.SubElements && !isNullPointer(*typeNameIn, *valueIn))
|
||||
rc |= HasChildren;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* The special type dumpers have an integer return code meaning:
|
||||
* 0: ok
|
||||
* 1: Dereferencing or retrieving memory failed, this is out of scope,
|
||||
* do not try to query further.
|
||||
* > 1: A structural error was encountered, that is, the implementation
|
||||
* of the class changed (Qt or say, a different STL implementation).
|
||||
* Visibly warn about it.
|
||||
* To add further types, have a look at the toString() output of the
|
||||
* symbol group. */
|
||||
|
||||
static QString msgStructuralError(const QString &name, const QString &type, int code)
|
||||
{
|
||||
return QString::fromLatin1("Warning: Internal dumper for '%1' (%2) failed with %3.").arg(name, type).arg(code);
|
||||
}
|
||||
|
||||
static inline bool isStdStringOrPointer(const QString &type)
|
||||
{
|
||||
#define STD_WSTRING "std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >"
|
||||
#define STD_STRING "std::basic_string<char,std::char_traits<char>,std::allocator<char> >"
|
||||
return type.endsWith(QLatin1String(STD_STRING))
|
||||
|| type.endsWith(QLatin1String(STD_STRING" *"))
|
||||
|| type.endsWith(QLatin1String(STD_WSTRING))
|
||||
|| type.endsWith(QLatin1String(STD_WSTRING" *"));
|
||||
#undef STD_WSTRING
|
||||
#undef STD_STRING
|
||||
}
|
||||
|
||||
unsigned SymbolGroupContext::dumpValue(unsigned long index,
|
||||
QString *inameIn,
|
||||
QString *nameIn,
|
||||
ULONG64 *addrIn,
|
||||
ULONG *typeIdIn,
|
||||
QString *typeNameIn,
|
||||
QString *valueIn)
|
||||
{
|
||||
unsigned rc = dumpValueRaw(index, inameIn, nameIn, addrIn, typeIdIn,
|
||||
typeNameIn, valueIn);
|
||||
do {
|
||||
// Is this a previously detected Null-Pointer or out of scope
|
||||
if ( (rc & OutOfScope) || !(rc & HasChildren) )
|
||||
break;
|
||||
// QString
|
||||
if (typeNameIn->endsWith(QLatin1String("QString")) || typeNameIn->endsWith(QLatin1String("QString *"))) {
|
||||
const int drc = dumpQString(index, *inameIn, valueIn);
|
||||
switch (drc) {
|
||||
case 0:
|
||||
rc |= InternalDumperSucceeded;
|
||||
rc &= ~HasChildren;
|
||||
break;
|
||||
case 1:
|
||||
rc |= InternalDumperError;
|
||||
break;
|
||||
default:
|
||||
rc |= InternalDumperFailed;
|
||||
qWarning("%s\n", qPrintable(msgStructuralError(*inameIn, *typeNameIn, drc)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// StdString
|
||||
if (isStdStringOrPointer(*typeNameIn)) {
|
||||
const int drc = dumpStdString(index, *inameIn, valueIn);
|
||||
switch (drc) {
|
||||
case 0:
|
||||
rc |= InternalDumperSucceeded;
|
||||
rc &= ~HasChildren;
|
||||
break;
|
||||
case 1:
|
||||
rc |= InternalDumperError;
|
||||
break;
|
||||
default:
|
||||
rc |= InternalDumperFailed;
|
||||
qWarning("%s\n", qPrintable(msgStructuralError(*inameIn, *typeNameIn, drc)));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
} while (false);
|
||||
if (debugInternalDumpers) {
|
||||
QString msg;
|
||||
QTextStream str(&msg);
|
||||
str.setIntegerBase(16);
|
||||
str << "SymbolGroupContext::dump rc=0x" << rc;
|
||||
str.setIntegerBase(10);
|
||||
str << " Type='" << *typeNameIn;
|
||||
str << " (" << *typeIdIn << ") Name='" << *nameIn << "' Value='" << *valueIn << '\'';
|
||||
qDebug("%s", qPrintable(msg));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool SymbolGroupContext::getDecimalIntValue(QString stringValue, qint64 *value)
|
||||
{
|
||||
// Strip '0n<digits>' format specifier that occurs
|
||||
// with Debugging tools v6.12 or later
|
||||
if (stringValue.startsWith(QLatin1String("0n")))
|
||||
stringValue.remove(0, 2);
|
||||
// Chop off character values (0n97 'a')
|
||||
const int blankPos = stringValue.indexOf(QLatin1Char(' '));
|
||||
if (blankPos != -1)
|
||||
stringValue.truncate(blankPos);
|
||||
bool ok;
|
||||
*value = stringValue.toInt(&ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Get integer value of symbol group
|
||||
static inline bool getSG_DecimalIntValue(CIDebugSymbolGroup *sg, int index, qint64 *value)
|
||||
{
|
||||
const QString valueS = getSymbolString(sg, &IDebugSymbolGroup2::GetSymbolValueTextWide, index);
|
||||
return SymbolGroupContext::getDecimalIntValue(valueS, value);
|
||||
}
|
||||
|
||||
// Get pointer value of symbol group ("0xAAB")
|
||||
// Note that this is on "00000000`0250124a" on 64bit systems.
|
||||
static inline bool getSG_UnsignedHexValue(CIDebugSymbolGroup *sg, int index, quint64 *value)
|
||||
{
|
||||
const QString stringValue = getSymbolString(sg, &IDebugSymbolGroup2::GetSymbolValueTextWide, index);
|
||||
return SymbolGroupContext::getUnsignedHexValue(stringValue, value);
|
||||
}
|
||||
|
||||
enum { maxStringLength = 4096 };
|
||||
|
||||
int SymbolGroupContext::dumpQString(unsigned long index,
|
||||
const QString &iname,
|
||||
QString *valueIn)
|
||||
{
|
||||
valueIn->clear();
|
||||
QString errorMessage;
|
||||
// Expand string and it's "d" (step over 'static null')
|
||||
if (!expandSymbol(iname, index, &errorMessage))
|
||||
return 2;
|
||||
const unsigned long dIndex = index + 4;
|
||||
if (!expandSymbol(dIndex, &errorMessage))
|
||||
return 3;
|
||||
const unsigned long sizeIndex = dIndex + 3;
|
||||
const unsigned long arrayIndex = dIndex + 4;
|
||||
// Get size and pointer
|
||||
qint64 size;
|
||||
if (!getSG_DecimalIntValue(m_symbolGroup, sizeIndex, &size))
|
||||
return 4;
|
||||
quint64 array;
|
||||
if (!getSG_UnsignedHexValue(m_symbolGroup, arrayIndex, &array))
|
||||
return 5;
|
||||
// Fetch
|
||||
const bool truncated = size > maxStringLength;
|
||||
if (truncated)
|
||||
size = maxStringLength;
|
||||
const QChar doubleQuote = QLatin1Char('"');
|
||||
if (size > 0) {
|
||||
valueIn->append(doubleQuote);
|
||||
// Should this ever be a remote debugger, need to check byte order.
|
||||
unsigned short *buf = new unsigned short[size + 1];
|
||||
unsigned long bytesRead;
|
||||
const HRESULT hr = m_dataSpaces->ReadVirtual(array, buf, size * ULONG(sizeof(unsigned short)), &bytesRead);
|
||||
if (FAILED(hr)) {
|
||||
delete [] buf;
|
||||
return 1;
|
||||
}
|
||||
buf[bytesRead / sizeof(unsigned short)] = 0;
|
||||
valueIn->append(QString::fromUtf16(buf));
|
||||
delete [] buf;
|
||||
if (truncated)
|
||||
valueIn->append(QLatin1String("..."));
|
||||
valueIn->append(doubleQuote);
|
||||
} else if (size == 0) {
|
||||
*valueIn = QString(doubleQuote) + doubleQuote;
|
||||
} else {
|
||||
*valueIn = QLatin1String("Invalid QString");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SymbolGroupContext::dumpStdString(unsigned long index,
|
||||
const QString &inameIn,
|
||||
QString *valueIn)
|
||||
|
||||
{
|
||||
QString errorMessage;
|
||||
// Expand string ->string_val->_bx.
|
||||
if (!expandSymbol(inameIn, index, &errorMessage))
|
||||
return 1;
|
||||
int sizeIndex = -1;
|
||||
int bufIndex = -1;
|
||||
if (m_symbolParameters.at(index).SubElements >= 3
|
||||
&& m_inameIndexMap.key(index + 3).endsWith(QLatin1String("Bx"))) {
|
||||
// Up to MSVC 2008
|
||||
const int bxIndex = index + 3;
|
||||
if (m_symbolParameters.at(bxIndex).SubElements < 2
|
||||
|| !expandSymbol(index + 3, &errorMessage))
|
||||
return 2;
|
||||
// Check if size is something sane
|
||||
sizeIndex = index + 6;
|
||||
bufIndex = index + 4;
|
||||
} else {
|
||||
// MSVC10 onwards: Large nested string_val structure containing Bx
|
||||
if (m_symbolParameters.at(index + 1).SubElements < 5
|
||||
|| !expandSymbol(index + 1, &errorMessage))
|
||||
return 3;
|
||||
const int bxIndex = index + 3;
|
||||
if (m_symbolParameters.at(bxIndex).SubElements < 3
|
||||
|| !m_inameIndexMap.key(bxIndex).endsWith(QLatin1String("Bx"))
|
||||
|| !expandSymbol(bxIndex, &errorMessage))
|
||||
return 4;
|
||||
sizeIndex = index + 7;
|
||||
bufIndex = index + 4;
|
||||
}
|
||||
if (sizeIndex < 0 || bufIndex < 0
|
||||
|| sizeIndex >= m_symbolParameters.size() || bufIndex >= m_symbolParameters.size())
|
||||
return 5;
|
||||
// Extract size and buffer
|
||||
qint64 size;
|
||||
if (!getSG_DecimalIntValue(m_symbolGroup, sizeIndex, &size))
|
||||
return 6;
|
||||
if (size < 0)
|
||||
return 1;
|
||||
// Just copy over the value of the buf[]-array, which should be the string
|
||||
*valueIn = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, bufIndex);
|
||||
const QChar doubleQuote = QLatin1Char('"');
|
||||
const int quotePos = valueIn->indexOf(doubleQuote);
|
||||
if (quotePos == -1)
|
||||
return 7;
|
||||
valueIn->remove(0, quotePos);
|
||||
if (valueIn->size() > maxStringLength) {
|
||||
valueIn->truncate(maxStringLength);
|
||||
valueIn->append(QLatin1String("...\""));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SymbolGroupContext::assignValue(const QString &iname, const QString &value,
|
||||
QString *newValue, QString *errorMessage)
|
||||
{
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << '\n' << iname << value;
|
||||
const NameIndexMap::const_iterator it = m_inameIndexMap.constFind(iname);
|
||||
if (it == m_inameIndexMap.constEnd()) {
|
||||
*errorMessage = msgSymbolNotFound(iname);
|
||||
return false;
|
||||
}
|
||||
const unsigned long index = it.value();
|
||||
const HRESULT hr = m_symbolGroup->WriteSymbolWide(index, reinterpret_cast<PCWSTR>(value.utf16()));
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = QString::fromLatin1("Unable to assign '%1' to '%2': %3").
|
||||
arg(value, iname, CdbCore::msgComFailed("WriteSymbolWide", hr));
|
||||
return false;
|
||||
}
|
||||
if (newValue)
|
||||
*newValue = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace CdbCore
|
||||
@@ -1,191 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef SYMBOLGROUPCONTEXT_H
|
||||
#define SYMBOLGROUPCONTEXT_H
|
||||
|
||||
#include "cdbcom.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QPair>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QSet>
|
||||
|
||||
namespace CdbCore {
|
||||
|
||||
/* A thin wrapper around the IDebugSymbolGroup2 interface which represents
|
||||
* a flat list of symbols using an index (for example, belonging to a stack
|
||||
* frame). It uses the hierarchical naming convention of WatchHandler as in:
|
||||
* "local" (invisible root)
|
||||
* "local.string" (local class variable)
|
||||
* "local.string.data" (class member)
|
||||
* and maintains a mapping iname -> index.
|
||||
* IDebugSymbolGroup2 can "expand" expandable symbols, inserting them into the
|
||||
* flat list after their parent.
|
||||
*
|
||||
* Note the pecularity of IDebugSymbolGroup2 with regard to pointed to items:
|
||||
* 1) A pointer to a POD (say int *) will expand to a pointed-to integer named '*'.
|
||||
* 2) A pointer to a class (QString *), will expand to the class members right away,
|
||||
* omitting the '*' derefenced item. That is a problem since the dumpers trigger
|
||||
* only on the derefenced item, so, additional handling is required.
|
||||
*/
|
||||
|
||||
class SymbolGroupContext
|
||||
{
|
||||
Q_DISABLE_COPY(SymbolGroupContext);
|
||||
protected:
|
||||
explicit SymbolGroupContext(const QString &prefix,
|
||||
CIDebugSymbolGroup *symbolGroup,
|
||||
CIDebugDataSpaces *dataSpaces,
|
||||
const QStringList &uninitializedVariables = QStringList());
|
||||
bool init(QString *errorMessage);
|
||||
|
||||
public:
|
||||
virtual ~SymbolGroupContext();
|
||||
static SymbolGroupContext *create(const QString &prefix,
|
||||
CIDebugSymbolGroup *symbolGroup,
|
||||
CIDebugDataSpaces *dataSpaces,
|
||||
const QStringList &uninitializedVariables,
|
||||
QString *errorMessage);
|
||||
|
||||
QString prefix() const { return m_prefix; }
|
||||
int size() const { return m_symbolParameters.size(); }
|
||||
|
||||
// Format a shadowed variable name/iname using a format taking two arguments:
|
||||
// "x <shadowed n"
|
||||
QString shadowedNameFormat() const;
|
||||
void setShadowedNameFormat(const QString &);
|
||||
|
||||
bool assignValue(const QString &iname, const QString &value,
|
||||
QString *newValue /* = 0 */, QString *errorMessage);
|
||||
|
||||
bool lookupPrefix(const QString &prefix, unsigned long *index) const;
|
||||
|
||||
enum SymbolState { LeafSymbol, ExpandedSymbol, CollapsedSymbol };
|
||||
SymbolState symbolState(unsigned long index) const;
|
||||
SymbolState symbolState(const QString &prefix) const;
|
||||
|
||||
inline bool isExpanded(unsigned long index) const { return symbolState(index) == ExpandedSymbol; }
|
||||
inline bool isExpanded(const QString &prefix) const { return symbolState(prefix) == ExpandedSymbol; }
|
||||
|
||||
// Dump name/type of an entry running the internal dumpers for known types
|
||||
// May expand symbols.
|
||||
|
||||
enum ValueFlags {
|
||||
HasChildren = 0x1,
|
||||
OutOfScope = 0x2,
|
||||
InternalDumperSucceeded = 0x4,
|
||||
InternalDumperError = 0x8, // Hard error
|
||||
InternalDumperFailed = 0x10,
|
||||
InternalDumperMask = InternalDumperSucceeded|InternalDumperError|InternalDumperFailed
|
||||
};
|
||||
|
||||
unsigned dumpValue(unsigned long index, QString *inameIn, QString *nameIn, ULONG64 *addrIn,
|
||||
ULONG *typeIdIn, QString *typeNameIn, QString *valueIn);
|
||||
|
||||
// For 64bit values (returned as dec), potentially '0n..'.
|
||||
static bool getDecimalIntValue(QString stringValue, qint64 *value);
|
||||
// For pointers and 64bit values (returned as hex)
|
||||
static bool getUnsignedHexValue(QString stringValue, quint64 *value, int *endPos = 0);
|
||||
// Convenience to return an integer (hex/decimal) as matching variant (signed/unsigned).
|
||||
static QVariant getIntValue(const QString &stringValue);
|
||||
|
||||
// Null-check for pointers
|
||||
static bool isNullPointer(const QString &type , QString valueS);
|
||||
// Symbol group values may contain template types which is not desired.
|
||||
static QString removeInnerTemplateType(QString value);
|
||||
|
||||
QString debugToString(bool verbose = false) const;
|
||||
QString toString(); // calls dump/potentially expands
|
||||
|
||||
// Filter out locale variables and arguments
|
||||
inline static bool isSymbolDisplayable(const DEBUG_SYMBOL_PARAMETERS &p);
|
||||
|
||||
protected:
|
||||
bool getChildSymbolsPosition(const QString &prefix,
|
||||
unsigned long *startPos,
|
||||
unsigned long *parentId,
|
||||
QString *errorMessage);
|
||||
|
||||
const DEBUG_SYMBOL_PARAMETERS &symbolParameterAt(int i) const { return m_symbolParameters.at(i); }
|
||||
|
||||
private:
|
||||
typedef QMap<QString, unsigned long> NameIndexMap;
|
||||
|
||||
void clear();
|
||||
bool expandSymbol(const QString &prefix, unsigned long index, QString *errorMessage);
|
||||
bool expandSymbol(unsigned long index, QString *errorMessage);
|
||||
void populateINameIndexMap(const QString &prefix, unsigned long parentId, unsigned long end);
|
||||
QString symbolINameAt(unsigned long index) const;
|
||||
inline QString formatShadowedName(const QString &name, int n) const;
|
||||
|
||||
// Raw dump of an entry (without dumpers)
|
||||
unsigned dumpValueRaw(unsigned long index, QString *inameIn, QString *nameIn,
|
||||
ULONG64 *addrIn, ULONG *typeIdIn, QString *typeNameIn,
|
||||
QString *valueIn) const;
|
||||
|
||||
int dumpQString(unsigned long index, const QString &inameIn, QString *valueIn);
|
||||
int dumpStdString(unsigned long index, const QString &inameIn, QString *valueIn);
|
||||
|
||||
inline DEBUG_SYMBOL_PARAMETERS *symbolParameters() { return &(*m_symbolParameters.begin()); }
|
||||
inline const DEBUG_SYMBOL_PARAMETERS *symbolParameters() const { return &(*m_symbolParameters.constBegin()); }
|
||||
|
||||
const QString m_prefix;
|
||||
const QChar m_nameDelimiter;
|
||||
const QSet<QString> m_uninitializedVariables;
|
||||
|
||||
CIDebugSymbolGroup *m_symbolGroup;
|
||||
CIDebugDataSpaces *m_dataSpaces;
|
||||
NameIndexMap m_inameIndexMap;
|
||||
QVector<DEBUG_SYMBOL_PARAMETERS> m_symbolParameters;
|
||||
int m_unnamedSymbolNumber;
|
||||
QString m_shadowedNameFormat;
|
||||
};
|
||||
|
||||
// Filter out locale variables and arguments
|
||||
bool SymbolGroupContext::isSymbolDisplayable(const DEBUG_SYMBOL_PARAMETERS &p)
|
||||
{
|
||||
if (p.Flags & (DEBUG_SYMBOL_IS_LOCAL|DEBUG_SYMBOL_IS_ARGUMENT))
|
||||
return true;
|
||||
// Do not display static members.
|
||||
if (p.Flags & DEBUG_SYMBOL_READ_ONLY)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace CdbCore
|
||||
|
||||
#endif // SYMBOLGROUPCONTEXT_H
|
||||
@@ -54,6 +54,7 @@
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <texteditor/itexteditor.h>
|
||||
#include <projectexplorer/toolchain.h>
|
||||
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/winutils.h>
|
||||
@@ -248,14 +249,26 @@ static inline bool validMode(DebuggerStartMode sm)
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline QString msgCdbDisabled(ProjectExplorer::ToolChainType tc)
|
||||
{
|
||||
return CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
|
||||
arg(ProjectExplorer::ToolChain::toolChainName(tc));
|
||||
}
|
||||
|
||||
// Accessed by RunControlFactory
|
||||
DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp, QString *errorMessage)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
CdbOptionsPage *op = CdbOptionsPage::instance();
|
||||
QTC_ASSERT(op, return 0);
|
||||
if (validMode(sp.startMode))
|
||||
return new CdbEngine(sp, op->options());
|
||||
if (!op || !op->options()->isValid()) {
|
||||
*errorMessage = msgCdbDisabled(static_cast<ProjectExplorer::ToolChainType>(sp.toolChainType));
|
||||
return 0;
|
||||
}
|
||||
if (!validMode(sp.startMode)) {
|
||||
*errorMessage = CdbEngine::tr("The CDB debug engine does not support start mode %1.").arg(sp.startMode);
|
||||
return 0;
|
||||
}
|
||||
return new CdbEngine(sp, op->options());
|
||||
#else
|
||||
Q_UNUSED(sp)
|
||||
#endif
|
||||
@@ -266,12 +279,38 @@ DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp, QString *erro
|
||||
bool isCdbEngineEnabled()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->enabled;
|
||||
return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool checkCdbConfiguration(int toolChainI, QString *errorMsg, QString *settingsPage)
|
||||
{
|
||||
const ProjectExplorer::ToolChainType toolChain = static_cast<ProjectExplorer::ToolChainType>(toolChainI);
|
||||
switch (toolChain) {
|
||||
case ProjectExplorer::ToolChain_MinGW: // Do our best
|
||||
case ProjectExplorer::ToolChain_MSVC:
|
||||
case ProjectExplorer::ToolChain_WINCE:
|
||||
case ProjectExplorer::ToolChain_OTHER:
|
||||
case ProjectExplorer::ToolChain_UNKNOWN:
|
||||
case ProjectExplorer::ToolChain_INVALID:
|
||||
if (!isCdbEngineEnabled()) {
|
||||
*errorMsg = msgCdbDisabled(toolChain);
|
||||
*settingsPage = CdbOptionsPage::settingsId();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//: %1 is something like "GCCE" or "Intel C++ Compiler (Linux)" (see ToolChain context)
|
||||
*errorMsg = CdbEngine::tr("The CDB debug engine does not support the %1 toolchain.").
|
||||
arg(ProjectExplorer::ToolChain::toolChainName(toolChain));
|
||||
*settingsPage = CdbOptionsPage::settingsId();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void addCdb2OptionPages(QList<Core::IOptionsPage *> *opts)
|
||||
{
|
||||
opts->push_back(new CdbOptionsPage);
|
||||
|
||||
@@ -47,6 +47,9 @@ struct CdbOptions
|
||||
{
|
||||
public:
|
||||
CdbOptions();
|
||||
|
||||
bool isValid() const { return enabled && !executable.isEmpty(); }
|
||||
|
||||
void clearExecutable();
|
||||
void clear();
|
||||
|
||||
|
||||
@@ -394,7 +394,7 @@ QString CdbOptionsPage::settingsId()
|
||||
|
||||
QString CdbOptionsPage::displayName() const
|
||||
{
|
||||
return tr("CDB (new, experimental)");
|
||||
return tr("CDB");
|
||||
}
|
||||
|
||||
QString CdbOptionsPage::category() const
|
||||
|
||||
@@ -126,7 +126,6 @@ SOURCES += registerpostmortemaction.cpp
|
||||
LIBS *= -lole32 \
|
||||
-lshell32
|
||||
}
|
||||
include(cdb/cdb.pri)
|
||||
include(cdb2/cdb2.pri)
|
||||
include(gdb/gdb.pri)
|
||||
include(script/script.pri)
|
||||
|
||||
@@ -418,9 +418,6 @@ namespace Internal {
|
||||
void addGdbOptionPages(QList<IOptionsPage*> *opts);
|
||||
void addScriptOptionPages(QList<IOptionsPage*> *opts);
|
||||
void addTcfOptionPages(QList<IOptionsPage*> *opts);
|
||||
#ifdef CDB_ENABLED
|
||||
void addCdbOptionPages(QList<IOptionsPage*> *opts);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_LLDB
|
||||
void addLldbOptionPages(QList<IOptionsPage*> *opts);
|
||||
@@ -3018,10 +3015,6 @@ void DebuggerPluginPrivate::extensionsInitialized()
|
||||
QList<Core::IOptionsPage *> engineOptionPages;
|
||||
if (m_cmdLineEnabledEngines & GdbEngineType)
|
||||
addGdbOptionPages(&engineOptionPages);
|
||||
#ifdef CDB_ENABLED
|
||||
if (m_cmdLineEnabledEngines & CdbEngineType)
|
||||
addCdbOptionPages(&engineOptionPages);
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
Debugger::Cdb::addCdb2OptionPages(&engineOptionPages);
|
||||
#endif
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace Debugger {
|
||||
namespace Cdb {
|
||||
DebuggerEngine *createCdbEngine(const DebuggerStartParameters &, QString *error);
|
||||
bool isCdbEngineEnabled(); // Check the configuration page
|
||||
bool checkCdbConfiguration(int toolChainI, QString *errorMsg, QString *settingsPage);
|
||||
}
|
||||
|
||||
namespace Internal {
|
||||
@@ -85,32 +86,6 @@ DebuggerEngine *createLldbEngine(const DebuggerStartParameters &);
|
||||
|
||||
extern QString msgNoBinaryForToolChain(int tc);
|
||||
|
||||
// FIXME: Outdated?
|
||||
// The createCdbEngine function takes a list of options pages it can add to.
|
||||
// This allows for having a "enabled" toggle on the page independently
|
||||
// of the engine. That's good for not enabling the related ActiveX control
|
||||
// unnecessarily.
|
||||
|
||||
#ifdef CDB_ENABLED
|
||||
|
||||
DebuggerEngine *createCdbEngine(const DebuggerStartParameters &, QString *error);
|
||||
bool checkCdbConfiguration(int toolChain, QString *errorMsg, QString *settingsPage);
|
||||
bool isCdbEngineEnabled(); // Check the configuration page
|
||||
|
||||
#else
|
||||
|
||||
DebuggerEngine *createCdbEngine(const DebuggerStartParameters &, QString *)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool checkCdbConfiguration(int, QString *, QString *)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static QString msgEngineNotAvailable(const char *engine)
|
||||
{
|
||||
return DebuggerPlugin::tr("The application requires the debugger engine '%1', "
|
||||
@@ -343,11 +318,7 @@ DebuggerRunControl::DebuggerRunControl(RunConfiguration *runConfiguration,
|
||||
d->m_engine = createScriptEngine(sp);
|
||||
break;
|
||||
case CdbEngineType:
|
||||
// Try new engine, fall back to old.
|
||||
if (Cdb::isCdbEngineEnabled())
|
||||
d->m_engine = Cdb::createCdbEngine(sp, &d->m_errorMessage);
|
||||
else
|
||||
d->m_engine = Internal::createCdbEngine(sp, &d->m_errorMessage);
|
||||
d->m_engine = Cdb::createCdbEngine(sp, &d->m_errorMessage);
|
||||
break;
|
||||
case PdbEngineType:
|
||||
d->m_engine = createPdbEngine(sp);
|
||||
@@ -445,7 +416,7 @@ bool DebuggerRunControl::checkDebugConfiguration(int toolChain,
|
||||
}
|
||||
break;
|
||||
case ProjectExplorer::ToolChain_MSVC:
|
||||
success = checkCdbConfiguration(toolChain, errorMessage, settingsPage);
|
||||
success = Cdb::checkCdbConfiguration(toolChain, errorMessage, settingsPage);
|
||||
if (!success) {
|
||||
*errorMessage += msgEngineNotAvailable("Cdb");
|
||||
if (settingsPage)
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Cdb {
|
||||
DebuggerEngine *createCdbEngine(const DebuggerStartParameters &, QString *);
|
||||
}
|
||||
namespace Internal {
|
||||
|
||||
const int ConnectionWaitTimeMs = 5000;
|
||||
|
||||
DebuggerEngine *createCdbEngine(const DebuggerStartParameters &, QString *);
|
||||
DebuggerEngine *createGdbEngine(const DebuggerStartParameters &);
|
||||
DebuggerEngine *createQmlEngine(const DebuggerStartParameters &);
|
||||
|
||||
@@ -58,7 +60,7 @@ QmlCppEngine::QmlCppEngine(const DebuggerStartParameters &sp)
|
||||
d->m_cppEngine = createGdbEngine(sp);
|
||||
} else {
|
||||
QString errorMessage;
|
||||
d->m_cppEngine = createCdbEngine(sp, &errorMessage);
|
||||
d->m_cppEngine = Debugger::Cdb::createCdbEngine(sp, &errorMessage);
|
||||
if (!d->m_cppEngine) {
|
||||
qWarning("%s", qPrintable(errorMessage));
|
||||
return;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
This directory contains a command line tool to the Debugging Tools for
|
||||
Windows for testing/scripting purposes.
|
||||
@@ -1,32 +0,0 @@
|
||||
# -------------------------------------------------
|
||||
# Project created by QtCreator 2010-01-22T10:11:10
|
||||
# -------------------------------------------------
|
||||
QT += core
|
||||
TARGET = ccdb
|
||||
CONFIG += console
|
||||
CONFIG -= app_bundle
|
||||
TEMPLATE = app
|
||||
|
||||
DEFINES += TEST_COMPILE
|
||||
# -- Add CDB core engine
|
||||
CDB_CORE = ../../../src/plugins/debugger/cdb
|
||||
include($$CDB_CORE/cdbcore.pri)
|
||||
INCLUDEPATH *= $$CDB_CORE
|
||||
|
||||
include(../../../qtcreator.pri)
|
||||
|
||||
# -- Add creator 'utils' lib
|
||||
CREATOR_LIB_LIB = ../../../lib/qtcreator
|
||||
LIBS *= -L$$CREATOR_LIB_LIB
|
||||
LIBS *= -l$$qtLibraryName(Utils)
|
||||
CREATOR_LIB_SRC = ../../../src/libs
|
||||
INCLUDEPATH *= $$CREATOR_LIB_SRC
|
||||
|
||||
# -- App sources
|
||||
SOURCES += main.cpp \
|
||||
cdbapplication.cpp \
|
||||
debugeventcallback.cpp \
|
||||
cdbpromptthread.cpp
|
||||
HEADERS += cdbapplication.h \
|
||||
debugeventcallback.h \
|
||||
cdbpromptthread.h
|
||||
@@ -1,379 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbapplication.h"
|
||||
#include "coreengine.h"
|
||||
#include "cdbdebugoutput.h"
|
||||
#include "cdbpromptthread.h"
|
||||
#include "debugeventcallback.h"
|
||||
#include "symbolgroupcontext.h"
|
||||
#include "stacktracecontext.h"
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cerrno>
|
||||
|
||||
const char usage[] =
|
||||
"CDB command line test tool\n\n"
|
||||
"ccdb <Options>\n"
|
||||
"Options: -p engine path\n"
|
||||
" -c initial_command_file\n";
|
||||
|
||||
class PrintfOutputHandler : public CdbCore::DebugOutputBase
|
||||
{
|
||||
public:
|
||||
PrintfOutputHandler() {}
|
||||
|
||||
protected:
|
||||
virtual void output(ULONG mask, const QString &message)
|
||||
{ std::printf("%10s: %s\n", maskDescription(mask), qPrintable(message)); }
|
||||
};
|
||||
|
||||
|
||||
// -------------- CdbApplication
|
||||
CdbApplication::CdbApplication(int argc, char *argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
m_engine(new CdbCore::CoreEngine),
|
||||
m_promptThread(0),
|
||||
m_processHandle(0)
|
||||
{
|
||||
}
|
||||
|
||||
CdbApplication::~CdbApplication()
|
||||
{
|
||||
}
|
||||
|
||||
CdbApplication::InitResult CdbApplication::init()
|
||||
{
|
||||
FILE *inputFile;
|
||||
if (!parseOptions(&inputFile)) {
|
||||
printf(usage);
|
||||
return InitUsageShown;
|
||||
}
|
||||
QString errorMessage;
|
||||
std::printf("Initializing engine %s...\n", qPrintable(m_engineDll));
|
||||
if (!m_engine->init(m_engineDll, &errorMessage)) {
|
||||
std::fprintf(stderr, "Failed: %s\n", qPrintable(errorMessage));
|
||||
return InitFailed;
|
||||
}
|
||||
m_engine->setDebugOutput(CdbCore::CoreEngine::DebugOutputBasePtr(new PrintfOutputHandler));
|
||||
DebugEventCallback *evt = new DebugEventCallback;
|
||||
connect(evt, SIGNAL(processAttached(void*)), this, SLOT(processAttached(void*)));
|
||||
m_engine->setDebugEventCallback(CdbCore::CoreEngine::DebugEventCallbackBasePtr(evt));
|
||||
m_engine->setExpressionSyntax(CdbCore::CoreEngine::CppExpressionSyntax);
|
||||
m_engine->setCodeLevel(CdbCore::CoreEngine::CodeLevelSource);
|
||||
connect(m_engine.data(), SIGNAL(watchTimerDebugEvent()), this, SLOT(debugEvent()));
|
||||
std::printf("Succeded.\n");
|
||||
// Prompt
|
||||
m_promptThread = new CdbPromptThread(inputFile, this);
|
||||
connect(m_promptThread, SIGNAL(finished()), this, SLOT(promptThreadTerminated()));
|
||||
connect(m_promptThread, SIGNAL(asyncCommand(int,QString)),
|
||||
this, SLOT(asyncCommand(int,QString)), Qt::QueuedConnection);
|
||||
connect(m_promptThread, SIGNAL(syncCommand(int,QString)),
|
||||
this, SLOT(syncCommand(int,QString)), Qt::BlockingQueuedConnection);
|
||||
connect(m_promptThread, SIGNAL(executionCommand(int,QString)),
|
||||
this, SLOT(executionCommand(int,QString)), Qt::BlockingQueuedConnection);
|
||||
connect(m_engine.data(), SIGNAL(watchTimerDebugEvent()), m_promptThread, SLOT(notifyDebugEvent()),
|
||||
Qt::QueuedConnection);
|
||||
m_promptThread->start();
|
||||
return InitOk;
|
||||
}
|
||||
|
||||
void CdbApplication::promptThreadTerminated()
|
||||
{
|
||||
QString errorMessage;
|
||||
m_engine->endSession(&errorMessage);
|
||||
std::printf("Terminating.\n");
|
||||
m_promptThread->wait();
|
||||
quit();
|
||||
}
|
||||
|
||||
bool CdbApplication::parseOptions(FILE **inputFile)
|
||||
{
|
||||
*inputFile = NULL;
|
||||
const QStringList args = QCoreApplication::arguments();
|
||||
const QStringList::const_iterator cend = args.constEnd();
|
||||
QStringList::const_iterator it = args.constBegin();
|
||||
for (++it; it != cend ; ++it) {
|
||||
const QString &a = *it;
|
||||
if (a.startsWith(QLatin1Char('-')) && a.size() >= 2) {
|
||||
switch (a.at(1).toAscii()) {
|
||||
case 'p':
|
||||
++it;
|
||||
if (it == cend) {
|
||||
std::fprintf(stderr, "Option -p is missing an argument.\n");
|
||||
return false;
|
||||
}
|
||||
m_engineDll = *it;
|
||||
break;
|
||||
case 'c':
|
||||
++it;
|
||||
if (it == cend) {
|
||||
std::fprintf(stderr, "Option -c is missing an argument.\n");
|
||||
return false;
|
||||
}
|
||||
*inputFile = std::fopen( it->toLocal8Bit().constData(), "r");
|
||||
if (*inputFile == NULL) {
|
||||
std::fprintf(stderr, "Cannot open %s: %s\n", qPrintable(*it), std::strerror(errno));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
std::fprintf(stderr, "Invalid option %s\n", qPrintable(a));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CdbApplication::asyncCommand(int command, const QString &arg)
|
||||
{
|
||||
Q_UNUSED(arg)
|
||||
QString errorMessage;
|
||||
switch (command) {
|
||||
case Async_Interrupt:
|
||||
if (m_processHandle) {
|
||||
if (m_engine->debugBreakProcess(m_processHandle, &errorMessage)) {
|
||||
std::printf("Stopped\n");
|
||||
} else {
|
||||
std::printf("%s\n", qPrintable(errorMessage));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CdbApplication::printFrame(const QString &arg)
|
||||
{
|
||||
QString errorMessage;
|
||||
do {
|
||||
if (m_stackTrace.isNull()) {
|
||||
errorMessage = QLatin1String("No trace.");
|
||||
break;
|
||||
}
|
||||
bool ok;
|
||||
const int frame = arg.toInt(&ok);
|
||||
if (!ok || frame < 0 || frame >= m_stackTrace->frameCount()) {
|
||||
errorMessage = QLatin1String("Invalid or out of range.");
|
||||
break;
|
||||
}
|
||||
CdbCore::SymbolGroupContext *ctx = m_stackTrace->symbolGroupContextAt(frame, &errorMessage);
|
||||
if (!ctx)
|
||||
break;
|
||||
printf("%s\n", qPrintable(ctx->toString()));
|
||||
} while (false);
|
||||
if (!errorMessage.isEmpty())
|
||||
printf("%s\n", qPrintable(errorMessage));
|
||||
}
|
||||
|
||||
// Return address or 0 on failure
|
||||
quint64 CdbApplication::addQueuedBreakPoint(const QString &arg, QString *errorMessage)
|
||||
{
|
||||
// Queue file:line
|
||||
const int cpos = arg.lastIndexOf(QLatin1Char(':'));
|
||||
if (cpos == -1) {
|
||||
*errorMessage = QString::fromLatin1("Syntax error in '%1': No colon.").arg(arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const QString fileName = arg.left(cpos);
|
||||
bool ok;
|
||||
const int lineNumber = arg.mid(cpos + 1).toInt(&ok);
|
||||
if (!ok || lineNumber < 1) {
|
||||
*errorMessage = QString::fromLatin1("Syntax error in '%1': No line number.").arg(arg);
|
||||
return 0;
|
||||
}
|
||||
CdbCore::BreakPoint bp;
|
||||
bp.address = m_engine->getSourceLineAddress(fileName, lineNumber, errorMessage);
|
||||
if (!bp.address)
|
||||
return 0;
|
||||
if (!bp.add(m_engine->interfaces().debugControl, errorMessage))
|
||||
return 0;
|
||||
return bp.address;
|
||||
}
|
||||
|
||||
void CdbApplication::syncCommand(int command, const QString &arg)
|
||||
{
|
||||
QString errorMessage;
|
||||
switch (command) {
|
||||
case Sync_EvalExpression: {
|
||||
QString value;
|
||||
QString type;
|
||||
std::printf("Evaluating '%s' in code level %d, syntax %d\n",
|
||||
qPrintable(arg), m_engine->codeLevel(), m_engine->expressionSyntax());
|
||||
if (m_engine->evaluateExpression(arg, &value, &type, &errorMessage)) {
|
||||
std::printf("[%s] %s\n", qPrintable(type), qPrintable(value));
|
||||
} else {
|
||||
std::printf("%s\n", qPrintable(errorMessage));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Sync_Queue: {
|
||||
const QString targs = arg.trimmed();
|
||||
if (targs.isEmpty()) {
|
||||
std::printf("Queue cleared\n");
|
||||
m_queuedCommands.clear();
|
||||
} else {
|
||||
std::printf("Queueing %s\n", qPrintable(targs));
|
||||
m_queuedCommands.push_back(targs);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Sync_QueueBreakPoint: {
|
||||
const QString targs = arg.trimmed();
|
||||
if (targs.isEmpty()) {
|
||||
std::printf("Breakpoint queue cleared\n");
|
||||
m_queuedBreakPoints.clear();
|
||||
} else {
|
||||
m_queuedBreakPoints.push_back(targs);
|
||||
std::printf("Queueing breakpoint %s\n", qPrintable(targs));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Sync_ListBreakPoints: {
|
||||
QList<CdbCore::BreakPoint> bps;
|
||||
if (CdbCore::BreakPoint::getBreakPoints(m_engine->interfaces().debugControl, &bps, &errorMessage)) {
|
||||
foreach (const CdbCore::BreakPoint &bp, bps)
|
||||
std::printf("%s\n", qPrintable(bp.expression()));
|
||||
} else {
|
||||
std::printf("BREAKPOINT LIST FAILED: %s\n", qPrintable(errorMessage));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Sync_PrintFrame:
|
||||
printFrame(arg);
|
||||
break;
|
||||
case Sync_OutputVersion:
|
||||
m_engine->outputVersion();
|
||||
break;
|
||||
case Sync_Python:
|
||||
break;
|
||||
case Unknown:
|
||||
std::printf("Executing '%s' in code level %d, syntax %d\n",
|
||||
qPrintable(arg), m_engine->codeLevel(), m_engine->expressionSyntax());
|
||||
if (!m_engine->executeDebuggerCommand(arg, &errorMessage))
|
||||
std::printf("%s\n", qPrintable(errorMessage));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CdbApplication::executionCommand(int command, const QString &arg)
|
||||
{
|
||||
bool ok = false;
|
||||
QString errorMessage;
|
||||
switch (command) {
|
||||
case Execution_StartBinary: {
|
||||
QStringList args = arg.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||
if (args.isEmpty()) {
|
||||
errorMessage = QLatin1String("Specify executable.");
|
||||
} else {
|
||||
std::printf("Starting\n");
|
||||
const QString binary = args.front();
|
||||
args.pop_front();
|
||||
ok = m_engine->startDebuggerWithExecutable(QString(), binary, args,
|
||||
QStringList(), &errorMessage);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Execution_Go:
|
||||
std::printf("Go\n");
|
||||
ok = m_engine->setExecutionStatus(DEBUG_STATUS_GO, &errorMessage);
|
||||
break;
|
||||
}
|
||||
if (ok) {
|
||||
m_engine->startWatchTimer();
|
||||
m_stackTrace = QSharedPointer<CdbCore::StackTraceContext>();
|
||||
} else {
|
||||
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
|
||||
}
|
||||
}
|
||||
|
||||
void CdbApplication::debugEvent()
|
||||
{
|
||||
QString errorMessage;
|
||||
std::printf("Debug event\n");
|
||||
|
||||
QVector<CdbCore::Thread> threads;
|
||||
QVector<CdbCore::StackFrame> threadFrames;
|
||||
ULONG currentThreadId;
|
||||
|
||||
if (CdbCore::StackTraceContext::getThreadList(m_engine->interfaces(), &threads, ¤tThreadId, &errorMessage)
|
||||
&& CdbCore::StackTraceContext::getStoppedThreadFrames(m_engine->interfaces(), currentThreadId, threads, &threadFrames, &errorMessage)) {
|
||||
const int count = threadFrames.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
printf("Thread #%02d ID %10d SYSID %10d %s\n", i,
|
||||
threads.at(i).id, threads.at(i).systemId, qPrintable(threadFrames.at(i).toString()));
|
||||
}
|
||||
} else {
|
||||
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
|
||||
}
|
||||
|
||||
CdbCore::StackTraceContext *trace =
|
||||
CdbCore::StackTraceContext::create(&m_engine->interfaces(),
|
||||
0xFFFF, &errorMessage);
|
||||
if (trace) {
|
||||
m_stackTrace = QSharedPointer<CdbCore::StackTraceContext>(trace);
|
||||
printf("%s\n", qPrintable(m_stackTrace->toString()));
|
||||
} else {
|
||||
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
|
||||
}
|
||||
}
|
||||
|
||||
void CdbApplication::processAttached(void *handle)
|
||||
{
|
||||
std::printf("### Process attached\n");
|
||||
m_processHandle = handle;
|
||||
QString errorMessage;
|
||||
// Commands
|
||||
foreach(const QString &qc, m_queuedCommands) {
|
||||
if (m_engine->executeDebuggerCommand(qc, &errorMessage)) {
|
||||
std::printf("'%s' [ok]\n", qPrintable(qc));
|
||||
} else {
|
||||
std::printf("%s\n", qPrintable(errorMessage));
|
||||
}
|
||||
}
|
||||
// Breakpoints
|
||||
foreach(const QString &bp, m_queuedBreakPoints) {
|
||||
if (const quint64 address = addQueuedBreakPoint(bp, &errorMessage)) {
|
||||
std::printf("'%s' 0x%lx [ok]\n", qPrintable(bp), address);
|
||||
} else {
|
||||
std::fprintf(stderr, "%s: %s\n",
|
||||
qPrintable(bp),
|
||||
qPrintable(errorMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBAPPLICATION_H
|
||||
#define CDBAPPLICATION_H
|
||||
|
||||
#include "corebreakpoint.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QScopedPointer>
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace CdbCore {
|
||||
class CoreEngine;
|
||||
class StackTraceContext;
|
||||
}
|
||||
|
||||
class CdbPromptThread;
|
||||
|
||||
class CdbApplication : public QCoreApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(CdbApplication)
|
||||
public:
|
||||
enum InitResult { InitFailed, InitUsageShown, InitOk };
|
||||
|
||||
CdbApplication(int argc, char *argv[]);
|
||||
~CdbApplication();
|
||||
|
||||
InitResult init();
|
||||
|
||||
private slots:
|
||||
void promptThreadTerminated();
|
||||
void asyncCommand(int command, const QString &arg);
|
||||
void syncCommand(int command, const QString &arg);
|
||||
void executionCommand(int command, const QString &arg);
|
||||
void debugEvent();
|
||||
void processAttached(void *handle);
|
||||
|
||||
private:
|
||||
bool parseOptions(FILE **inputFile);
|
||||
void printFrame(const QString &arg);
|
||||
quint64 addQueuedBreakPoint(const QString &arg, QString *errorMessage);
|
||||
|
||||
QString m_engineDll;
|
||||
QSharedPointer<CdbCore::CoreEngine> m_engine;
|
||||
QSharedPointer<CdbCore::StackTraceContext> m_stackTrace;
|
||||
CdbPromptThread *m_promptThread;
|
||||
QStringList m_queuedCommands;
|
||||
QStringList m_queuedBreakPoints;
|
||||
|
||||
void *m_processHandle;
|
||||
};
|
||||
|
||||
#endif // CDBAPPLICATION_H
|
||||
@@ -1,190 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbpromptthread.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
static const char help[] =
|
||||
"Special commands:\n\n"
|
||||
"H Display Help\n"
|
||||
"V Output version\n"
|
||||
"E expression Evaluate C++expression\n"
|
||||
"S binary args Start binary\n"
|
||||
"I Interrupt\n"
|
||||
"G Go\n"
|
||||
"Q cmd Queue command for execution in AttachProcess\n"
|
||||
"Q Clear command queue\n"
|
||||
"B file:line Queue a breakpoint for adding in AttachProcess\n"
|
||||
"B Clear breakpoint queue\n"
|
||||
"L List breakpoints\n"
|
||||
"F <n> Print stack frame <n>, 0 being top\n"
|
||||
"P <cmd> Run Python command\n"
|
||||
"W Synchronous wait for debug event\n"
|
||||
"\nThe remaining commands are passed to CDB.\n";
|
||||
|
||||
CdbPromptThread::CdbPromptThread(FILE *file, QObject *parent) :
|
||||
QThread(parent),
|
||||
m_waitingForDebugEvent(false),
|
||||
m_inputFile(file)
|
||||
{
|
||||
if (!m_inputFile)
|
||||
m_inputFile = stdin;
|
||||
}
|
||||
|
||||
void CdbPromptThread::run()
|
||||
{
|
||||
enum { bufSize =1024 };
|
||||
|
||||
QString cmd;
|
||||
char buf[bufSize];
|
||||
std::putc('>', stdout);
|
||||
// When reading from an input file, switch to stdin after reading it out
|
||||
while (true) {
|
||||
if (std::fgets(buf, bufSize, m_inputFile) == NULL) {
|
||||
if (m_inputFile == stdin) {
|
||||
break;
|
||||
} else {
|
||||
fclose(m_inputFile);
|
||||
m_inputFile = stdin;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
cmd += QString::fromLatin1(buf);
|
||||
if (cmd.endsWith(QLatin1Char('\n'))) {
|
||||
cmd.truncate(cmd.size() - 1);
|
||||
if (!cmd.isEmpty() && !handleCommand(cmd.trimmed()))
|
||||
break;
|
||||
cmd.clear();
|
||||
}
|
||||
std::putc('>', stdout);
|
||||
}
|
||||
if (m_inputFile != stdin)
|
||||
fclose(m_inputFile);
|
||||
}
|
||||
|
||||
// Determine the command
|
||||
static Command evaluateCommand(const QString &cmdToken)
|
||||
{
|
||||
if (cmdToken.size() == 1) {
|
||||
switch(cmdToken.at(0).toAscii()) {
|
||||
case 'I':
|
||||
return Async_Interrupt;
|
||||
case 'Q':
|
||||
return Sync_Queue;
|
||||
case 'B':
|
||||
return Sync_QueueBreakPoint;
|
||||
case 'L':
|
||||
return Sync_ListBreakPoints;
|
||||
case 'E':
|
||||
return Sync_EvalExpression;
|
||||
case 'G':
|
||||
return Execution_Go;
|
||||
case 'S':
|
||||
return Execution_StartBinary;
|
||||
case 'F':
|
||||
return Sync_PrintFrame;
|
||||
case 'P':
|
||||
return Sync_Python;
|
||||
case 'V':
|
||||
return Sync_OutputVersion;
|
||||
case 'W':
|
||||
return WaitCommand;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return UnknownCommand;
|
||||
}
|
||||
return UnknownCommand;
|
||||
}
|
||||
|
||||
// Chop off command and return argument list
|
||||
static Command parseCommand(QString *s)
|
||||
{
|
||||
if (s->isEmpty())
|
||||
return UnknownCommand;
|
||||
int firstBlank = s->indexOf(QLatin1Char(' '));
|
||||
// No further arguments
|
||||
if (firstBlank == -1) {
|
||||
const Command rc1 = evaluateCommand(*s);
|
||||
if (rc1 != UnknownCommand) // pass through debugger cmds
|
||||
s->clear();
|
||||
return rc1;
|
||||
}
|
||||
// Chop
|
||||
const Command rc = evaluateCommand(s->left(firstBlank));
|
||||
if (rc != UnknownCommand) { // pass through debugger cmds)
|
||||
int nextToken = firstBlank + 1;
|
||||
for ( ; nextToken < s->size() && s->at(nextToken).isSpace(); nextToken++) ;
|
||||
s->remove(0, nextToken);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void CdbPromptThread::notifyDebugEvent()
|
||||
{
|
||||
if (m_waitingForDebugEvent)
|
||||
m_debugEventWaitCondition.wakeAll();
|
||||
}
|
||||
|
||||
bool CdbPromptThread::handleCommand(QString cmd)
|
||||
{
|
||||
if (cmd == QLatin1String("q"))
|
||||
return false;
|
||||
if (cmd == QLatin1String("H")) {
|
||||
std::fputs(help, stdout);
|
||||
return true;
|
||||
}
|
||||
const Command c = parseCommand(&cmd);
|
||||
if (c == WaitCommand) {
|
||||
std::fputs("Waiting for debug event\n", stdout);
|
||||
m_debugEventMutex.lock();
|
||||
m_waitingForDebugEvent = true;
|
||||
m_debugEventWaitCondition.wait(&m_debugEventMutex);
|
||||
m_debugEventMutex.unlock();
|
||||
std::fputs("Debug event received\n", stdout);
|
||||
m_waitingForDebugEvent = false;
|
||||
return true;
|
||||
}
|
||||
if (c & AsyncCommand) {
|
||||
emit asyncCommand(c, cmd);
|
||||
return true;
|
||||
}
|
||||
if (c & ExecutionCommand) {
|
||||
emit executionCommand(c, cmd);
|
||||
return true;
|
||||
}
|
||||
// Let Unknown default to sync exeute
|
||||
emit syncCommand(c, cmd);
|
||||
return true;
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef PROMPTTHREAD_H
|
||||
#define PROMPTTHREAD_H
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QWaitCondition>
|
||||
#include <QtCore/QMutex>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
enum CommandTypeFlags {
|
||||
// Interrupt or something.
|
||||
AsyncCommand = 0x0010000,
|
||||
// Synchronous execution before next prompt,
|
||||
// eg eval expression. Connect with blocking slot.
|
||||
SyncCommand = 0x0020000,
|
||||
// Starts debuggee. Requires starting the debug event
|
||||
// watch timer afterwards.
|
||||
ExecutionCommand = 0x0040000
|
||||
};
|
||||
|
||||
enum Command {
|
||||
UnknownCommand = 0,
|
||||
Async_Interrupt = AsyncCommand|1,
|
||||
Sync_EvalExpression = SyncCommand|1,
|
||||
Sync_Queue = SyncCommand|2,
|
||||
Sync_QueueBreakPoint = SyncCommand|3,
|
||||
Sync_ListBreakPoints = SyncCommand|4,
|
||||
Sync_PrintFrame = SyncCommand|5,
|
||||
Sync_OutputVersion = SyncCommand|6,
|
||||
Sync_Python = SyncCommand|7,
|
||||
Execution_Go = ExecutionCommand|1,
|
||||
Execution_StartBinary = ExecutionCommand|2,
|
||||
WaitCommand = 0xFFFF
|
||||
};
|
||||
|
||||
class CdbPromptThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CdbPromptThread(FILE *file, QObject *parent = 0);
|
||||
|
||||
virtual void run();
|
||||
|
||||
public slots:
|
||||
void notifyDebugEvent();
|
||||
|
||||
signals:
|
||||
void asyncCommand(int command, const QString &arg);
|
||||
void syncCommand(int command, const QString &arg);
|
||||
void executionCommand(int command, const QString &arg);
|
||||
|
||||
private:
|
||||
bool handleCommand(QString);
|
||||
QWaitCondition m_debugEventWaitCondition;
|
||||
QMutex m_debugEventMutex;
|
||||
bool m_waitingForDebugEvent;
|
||||
FILE *m_inputFile;
|
||||
};
|
||||
|
||||
#endif // PROMPTTHREAD_H
|
||||
@@ -1,160 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "debugeventcallback.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
DebugEventCallback::DebugEventCallback()
|
||||
{
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallback::GetInterestMask(THIS_ __out PULONG mask)
|
||||
{
|
||||
*mask = DEBUG_EVENT_CREATE_PROCESS | DEBUG_EVENT_EXIT_PROCESS
|
||||
| DEBUG_EVENT_LOAD_MODULE | DEBUG_EVENT_UNLOAD_MODULE
|
||||
| DEBUG_EVENT_CREATE_THREAD | DEBUG_EVENT_EXIT_THREAD
|
||||
| DEBUG_EVENT_BREAKPOINT
|
||||
| DEBUG_EVENT_EXCEPTION;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2)
|
||||
{
|
||||
printf("Breakpoint hit\n");
|
||||
return S_OK;
|
||||
}
|
||||
STDMETHODIMP DebugEventCallback::Exception(
|
||||
THIS_
|
||||
__in PEXCEPTION_RECORD64 exc,
|
||||
__in ULONG firstChance
|
||||
)
|
||||
{
|
||||
printf("Exception %ul occurred first-chance: %ul\n", exc->ExceptionCode, firstChance);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallback::CreateThread(
|
||||
THIS_
|
||||
__in ULONG64 /* Handle */,
|
||||
__in ULONG64 /* DataOffset */,
|
||||
__in ULONG64 /* StartOffset */
|
||||
)
|
||||
{
|
||||
printf("Thread created\n");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallback::ExitThread(
|
||||
THIS_
|
||||
__in ULONG /* ExitCode */
|
||||
)
|
||||
{
|
||||
printf("Thread quit\n");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallback::CreateProcess(
|
||||
THIS_
|
||||
__in ULONG64 /* ImageFileHandle */,
|
||||
__in ULONG64 Handle,
|
||||
__in ULONG64 /* Offset */,
|
||||
__in ULONG /* ModuleSize */,
|
||||
__in_opt PCWSTR /* ModuleName */,
|
||||
__in_opt PCWSTR /* ImageName */,
|
||||
__in ULONG /* CheckSum */,
|
||||
__in ULONG /* TimeDateStamp */,
|
||||
__in ULONG64 /* InitialThreadHandle */,
|
||||
__in ULONG64 /* ThreadDataOffset */,
|
||||
__in ULONG64 /* StartOffset */
|
||||
)
|
||||
{
|
||||
printf("Process created %Ld\n", Handle);
|
||||
emit processAttached(reinterpret_cast<void*>(Handle));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallback::ExitProcess(
|
||||
THIS_
|
||||
__in ULONG /* ExitCode */
|
||||
)
|
||||
{
|
||||
printf("Process quit\n");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallback::LoadModule(
|
||||
THIS_
|
||||
__in ULONG64 /* ImageFileHandle */,
|
||||
__in ULONG64 /* Offset */,
|
||||
__in ULONG /* ModuleSize */,
|
||||
__in_opt PCWSTR /* ModuleName */,
|
||||
__in_opt PCWSTR /* ImageName */,
|
||||
__in ULONG /* CheckSum */,
|
||||
__in ULONG /* TimeDateStamp */
|
||||
)
|
||||
{
|
||||
printf("Module loaded\n");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallback::UnloadModule(
|
||||
THIS_
|
||||
__in_opt PCWSTR /* ImageName */,
|
||||
__in ULONG64 /* Offset */
|
||||
)
|
||||
{
|
||||
printf("Module unloaded\n");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DebugEventCallback::SystemError(
|
||||
THIS_
|
||||
__in ULONG Error,
|
||||
__in ULONG Level
|
||||
)
|
||||
{
|
||||
printf("System error %ul at %ul\n", Error, Level);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP DebugEventCallback::ChangeDebuggeeState(
|
||||
THIS_
|
||||
__in ULONG Flags,
|
||||
__in ULONG64 Argument
|
||||
)
|
||||
{
|
||||
printf("Debuggee state changed %ul %ul\n", Flags, Argument);
|
||||
return S_OK;
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef DEBUGEVENTCALLBACK_H
|
||||
#define DEBUGEVENTCALLBACK_H
|
||||
|
||||
#include "debugeventcallbackbase.h"
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class DebugEventCallback :
|
||||
public QObject,
|
||||
public CdbCore::DebugEventCallbackBase
|
||||
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DebugEventCallback();
|
||||
|
||||
STDMETHOD(GetInterestMask)(
|
||||
THIS_
|
||||
__out PULONG mask
|
||||
);
|
||||
|
||||
STDMETHOD(Breakpoint)(
|
||||
THIS_
|
||||
__in PDEBUG_BREAKPOINT2 Bp
|
||||
);
|
||||
|
||||
STDMETHOD(Exception)(
|
||||
THIS_
|
||||
__in PEXCEPTION_RECORD64 Exception,
|
||||
__in ULONG FirstChance
|
||||
);
|
||||
|
||||
STDMETHOD(CreateThread)(
|
||||
THIS_
|
||||
__in ULONG64 Handle,
|
||||
__in ULONG64 DataOffset,
|
||||
__in ULONG64 StartOffset
|
||||
);
|
||||
STDMETHOD(ExitThread)(
|
||||
THIS_
|
||||
__in ULONG ExitCode
|
||||
);
|
||||
|
||||
STDMETHOD(CreateProcess)(
|
||||
THIS_
|
||||
__in ULONG64 ImageFileHandle,
|
||||
__in ULONG64 Handle,
|
||||
__in ULONG64 BaseOffset,
|
||||
__in ULONG ModuleSize,
|
||||
__in_opt PCWSTR ModuleName,
|
||||
__in_opt PCWSTR ImageName,
|
||||
__in ULONG CheckSum,
|
||||
__in ULONG TimeDateStamp,
|
||||
__in ULONG64 InitialThreadHandle,
|
||||
__in ULONG64 ThreadDataOffset,
|
||||
__in ULONG64 StartOffset
|
||||
);
|
||||
|
||||
STDMETHOD(ExitProcess)(
|
||||
THIS_
|
||||
__in ULONG ExitCode
|
||||
);
|
||||
|
||||
STDMETHOD(LoadModule)(
|
||||
THIS_
|
||||
__in ULONG64 ImageFileHandle,
|
||||
__in ULONG64 BaseOffset,
|
||||
__in ULONG ModuleSize,
|
||||
__in_opt PCWSTR ModuleName,
|
||||
__in_opt PCWSTR ImageName,
|
||||
__in ULONG CheckSum,
|
||||
__in ULONG TimeDateStamp
|
||||
);
|
||||
|
||||
STDMETHOD(UnloadModule)(
|
||||
THIS_
|
||||
__in_opt PCWSTR ImageBaseName,
|
||||
__in ULONG64 BaseOffset
|
||||
);
|
||||
|
||||
STDMETHOD(SystemError)(
|
||||
THIS_
|
||||
__in ULONG Error,
|
||||
__in ULONG Level
|
||||
);
|
||||
|
||||
STDMETHOD(ChangeDebuggeeState)(
|
||||
THIS_
|
||||
__in ULONG Flags,
|
||||
__in ULONG64 Argument
|
||||
);
|
||||
|
||||
signals:
|
||||
void processAttached(void *h);
|
||||
};
|
||||
|
||||
#endif // DEBUGEVENTCALLBACK_H
|
||||
@@ -1,53 +0,0 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** No Commercial Usage
|
||||
**
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbapplication.h"
|
||||
#include <QtCore/QString>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
CdbApplication app(argc, argv);
|
||||
int rc = 0;
|
||||
|
||||
switch (app.init()) {
|
||||
case CdbApplication::InitOk:
|
||||
rc = app.exec();
|
||||
break;
|
||||
case CdbApplication::InitFailed:
|
||||
rc = 1;
|
||||
break;
|
||||
case CdbApplication::InitUsageShown:
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
Reference in New Issue
Block a user