Debugger: R.I.P. old CDB engine.

This commit is contained in:
Friedemann Kleint
2011-01-07 15:04:11 +01:00
parent 20aeec7305
commit 01d4c5a3d3
60 changed files with 54 additions and 13246 deletions
-97
View File
@@ -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
}
}
-246
View File
@@ -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
-77
View File
@@ -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
-149
View File
@@ -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
-64
View File
@@ -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
-62
View File
@@ -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
-137
View File
@@ -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
-54
View File
@@ -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
-61
View File
@@ -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"
-171
View File
@@ -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
-135
View File
@@ -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
-156
View File
@@ -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 &currentThreadState = 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
-288
View File
@@ -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(&parameters, 0, sizeof(DEBUG_MODULE_PARAMETERS));
hr = syms->GetModuleParameters(1, 0, 0u, &parameters);
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
-77
View File
@@ -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
-136
View File
@@ -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
-88
View File
@@ -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
-224
View File
@@ -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
-110
View File
@@ -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
-532
View File
@@ -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
-113
View File
@@ -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
-230
View File
@@ -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
-115
View File
@@ -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(&currentThread);
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
+43 -4
View File
@@ -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);
+3
View File
@@ -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
-1
View File
@@ -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)
-7
View File
@@ -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
+3 -32
View File
@@ -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)
+4 -2
View File
@@ -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;
-2
View File
@@ -1,2 +0,0 @@
This directory contains a command line tool to the Debugging Tools for
Windows for testing/scripting purposes.
-32
View File
@@ -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
-379
View File
@@ -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, &currentThreadId, &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));
}
}
}
-88
View File
@@ -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
-190
View File
@@ -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;
}
-93
View File
@@ -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
-160
View File
@@ -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;
}
-129
View File
@@ -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
-53
View File
@@ -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;
}