CDB: Extract SymbolGroupContext class into Core library.

Split for testing/scripting purposes.
This commit is contained in:
Friedemann Kleint
2010-02-04 13:19:49 +01:00
parent ccb946778e
commit fc972e44ef
28 changed files with 2870 additions and 1730 deletions

View File

@@ -0,0 +1,493 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "breakpoint.h"
#include "coreengine.h"
#include <utils/qtcassert.h>
#include <QtCore/QTextStream>
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QMap>
#include <psapi.h>
enum { debugBP = 0 };
namespace CdbCore {
// The CDB breakpoint expression syntax is:
// `foo.cpp:523`[ "condition"]
// module!function[ "condition"]
static const char sourceFileQuoteC = '`';
BreakPoint::BreakPoint() :
ignoreCount(0),
lineNumber(-1),
oneShot(false),
enabled(true)
{
}
int BreakPoint::compare(const BreakPoint& rhs) const
{
if (ignoreCount > rhs.ignoreCount)
return 1;
if (ignoreCount < rhs.ignoreCount)
return -1;
if (lineNumber > rhs.lineNumber)
return 1;
if (lineNumber < rhs.lineNumber)
return -1;
if (oneShot && !rhs.oneShot)
return 1;
if (!oneShot && rhs.oneShot)
return -1;
if (enabled && !rhs.enabled)
return 1;
if (!enabled && rhs.enabled)
return -1;
if (const int fileCmp = fileName.compare(rhs.fileName))
return fileCmp;
if (const int funcCmp = funcName.compare(rhs.funcName))
return funcCmp;
if (const int condCmp = condition.compare(rhs.condition))
return condCmp;
return 0;
}
void BreakPoint::clear()
{
ignoreCount = 0;
oneShot = false;
enabled = true;
clearExpressionData();
}
void BreakPoint::clearExpressionData()
{
fileName.clear();
condition.clear();
funcName.clear();
lineNumber = -1;
}
QDebug operator<<(QDebug dbg, const BreakPoint &bp)
{
QDebug nsp = dbg.nospace();
if (!bp.fileName.isEmpty()) {
nsp << "fileName='" << bp.fileName << ':' << bp.lineNumber << '\'';
} else {
nsp << "funcName='" << bp.funcName << '\'';
}
if (!bp.condition.isEmpty())
nsp << " condition='" << bp.condition << '\'';
if (bp.ignoreCount)
nsp << " ignoreCount=" << bp.ignoreCount;
if (bp.enabled)
nsp << " enabled";
if (bp.oneShot)
nsp << " oneShot";
return dbg;
}
QString BreakPoint::expression() const
{
// format the breakpoint expression (file/function and condition)
QString rc;
QTextStream str(&rc);
if (funcName.isEmpty()) {
const QChar sourceFileQuote = QLatin1Char(sourceFileQuoteC);
str << sourceFileQuote << QDir::toNativeSeparators(fileName) << QLatin1Char(':') << lineNumber << sourceFileQuote;
} else {
str << funcName;
}
if (!condition.isEmpty()) {
const QChar doubleQuote = QLatin1Char('"');
str << QLatin1Char(' ') << doubleQuote << condition << doubleQuote;
}
return rc;
}
bool BreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const
{
const QString expr = expression();
if (debugBP)
qDebug() << Q_FUNC_INFO << *this << expr;
const HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast<PCWSTR>(expr.utf16()));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to set breakpoint '%1' : %2").
arg(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr));
return false;
}
// Pass Count is ignoreCount + 1
ibp->SetPassCount(ignoreCount + 1u);
ULONG flags = 0;
if (enabled)
flags |= DEBUG_BREAKPOINT_ENABLED;
if (oneShot)
flags |= DEBUG_BREAKPOINT_ONE_SHOT;
ibp->AddFlags(flags);
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
{
IDebugBreakpoint2* ibp = 0;
if (address)
*address = 0;
if (id)
*id = 0;
HRESULT hr = debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, 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;
}
// Helper for normalizing file names:
// Map the device paths in a file name to back to drive letters
// "/Device/HarddiskVolume1/file.cpp" -> "C:/file.cpp"
static bool mapDeviceToDriveLetter(QString *s)
{
enum { bufSize = 512 };
// Retrieve drive letters and get their device names.
// Do not cache as it may change due to removable/network drives.
TCHAR driveLetters[bufSize];
if (!GetLogicalDriveStrings(bufSize-1, driveLetters))
return false;
TCHAR driveName[MAX_PATH];
TCHAR szDrive[3] = TEXT(" :");
for (const TCHAR *driveLetter = driveLetters; *driveLetter; driveLetter++) {
szDrive[0] = *driveLetter; // Look up each device name
if (QueryDosDevice(szDrive, driveName, MAX_PATH)) {
const QString deviceName = QString::fromUtf16(driveName);
if (s->startsWith(deviceName)) {
s->replace(0, deviceName.size(), QString::fromUtf16(szDrive));
return true;
}
}
}
return false;
}
// Helper for normalizing file names:
// Determine normalized case of a Windows file name (camelcase.cpp -> CamelCase.cpp)
// as the debugger reports lower case file names.
// Restriction: File needs to exists and be non-empty and will be to be opened/mapped.
// This is the MSDN-recommended way of doing that. The result should be cached.
static inline QString normalizeFileNameCaseHelper(const QString &f)
{
HANDLE hFile = CreateFile(f.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if(hFile == INVALID_HANDLE_VALUE)
return f;
// Get the file size. We need a non-empty file to map it.
DWORD dwFileSizeHi = 0;
DWORD dwFileSizeLo = GetFileSize(hFile, &dwFileSizeHi);
if (dwFileSizeLo == 0 && dwFileSizeHi == 0) {
CloseHandle(hFile);
return f;
}
// Create a file mapping object.
HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 1, NULL);
if (!hFileMap) {
CloseHandle(hFile);
return f;
}
// Create a file mapping to get the file name.
void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
if (!pMem) {
CloseHandle(hFileMap);
CloseHandle(hFile);
return f;
}
QString rc;
WCHAR pszFilename[MAX_PATH];
pszFilename[0] = 0;
// Get a file name of the form "/Device/HarddiskVolume1/file.cpp"
if (GetMappedFileName (GetCurrentProcess(), pMem, pszFilename, MAX_PATH)) {
rc = QString::fromUtf16(pszFilename);
if (!mapDeviceToDriveLetter(&rc))
rc.clear();
}
UnmapViewOfFile(pMem);
CloseHandle(hFileMap);
CloseHandle(hFile);
return rc.isEmpty() ? f : rc;
}
// 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)
{
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(normalizeFileNameCaseHelper(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;
}
void BreakPoint::clearNormalizeFileNameCache()
{
normalizedFileNameCache()->clear();
}
bool BreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
{
clear();
WCHAR wszBuf[MAX_PATH];
const HRESULT hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint: %1").
arg(CdbCore::msgComFailed("GetOffsetExpressionWide", hr));
return false;
}
// 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(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++) {
IDebugBreakpoint2 *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);
}
IDebugBreakpoint2 *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;
}
}

View File

@@ -0,0 +1,99 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#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. */
struct BreakPoint
{
BreakPoint();
int compare(const BreakPoint& rhs) const;
void clear();
void clearExpressionData();
QString expression() const;
// Apply parameters
bool apply(IDebugBreakpoint2 *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(IDebugBreakpoint2 *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 IDebugBreakpoint2 *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);
// Return a 'canonical' file (using '/' and capitalized drive letter)
static QString normalizeFileName(const QString &f);
static void clearNormalizeFileNameCache();
QString fileName; // short name of source file
QString condition; // condition associated with breakpoint
unsigned long ignoreCount; // ignore count associated with breakpoint
int lineNumber; // line in source file
QString funcName; // name of containing function
bool oneShot;
bool enabled;
};
QDebug operator<<(QDebug, const BreakPoint &bp);
inline bool operator==(const BreakPoint& b1, const BreakPoint& b2)
{ return b1.compare(b2) == 0; }
inline bool operator!=(const BreakPoint& b1, const BreakPoint& b2)
{ return b1.compare(b2) != 0; }
}
#endif // CDBCOREBREAKPOINTS_H

View File

@@ -10,7 +10,6 @@ HEADERS += \
$$PWD/cdbsymbolgroupcontext.h \ $$PWD/cdbsymbolgroupcontext.h \
$$PWD/cdbsymbolgroupcontext_tpl.h \ $$PWD/cdbsymbolgroupcontext_tpl.h \
$$PWD/cdbstacktracecontext.h \ $$PWD/cdbstacktracecontext.h \
$$PWD/cdbstackframecontext.h \
$$PWD/cdbbreakpoint.h \ $$PWD/cdbbreakpoint.h \
$$PWD/cdbmodules.h \ $$PWD/cdbmodules.h \
$$PWD/cdbassembler.h \ $$PWD/cdbassembler.h \
@@ -25,7 +24,6 @@ SOURCES += \
$$PWD/cdbdebugeventcallback.cpp \ $$PWD/cdbdebugeventcallback.cpp \
$$PWD/cdbdebugoutput.cpp \ $$PWD/cdbdebugoutput.cpp \
$$PWD/cdbsymbolgroupcontext.cpp \ $$PWD/cdbsymbolgroupcontext.cpp \
$$PWD/cdbstackframecontext.cpp \
$$PWD/cdbstacktracecontext.cpp \ $$PWD/cdbstacktracecontext.cpp \
$$PWD/cdbbreakpoint.cpp \ $$PWD/cdbbreakpoint.cpp \
$$PWD/cdbmodules.cpp \ $$PWD/cdbmodules.cpp \
@@ -37,6 +35,4 @@ SOURCES += \
$$PWD/cdbexceptionutils.cpp $$PWD/cdbexceptionutils.cpp
FORMS += $$PWD/cdboptionspagewidget.ui FORMS += $$PWD/cdboptionspagewidget.ui
LIBS+=-lpsapi
} }

View File

@@ -29,482 +29,13 @@
#include "cdbbreakpoint.h" #include "cdbbreakpoint.h"
#include "cdbmodules.h" #include "cdbmodules.h"
#include "breakhandler.h"
#include "cdbdebugengine_p.h"
#include <QtCore/QTextStream>
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QMap>
#include <psapi.h>
enum { debugBP = 0 };
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
// The CDB breakpoint expression syntax is: enum { debugBP = 0 };
// `foo.cpp:523`[ "condition"]
// module!function[ "condition"]
static const char sourceFileQuoteC = '`';
CDBBreakPoint::CDBBreakPoint() :
ignoreCount(0),
lineNumber(-1),
oneShot(false),
enabled(true)
{
}
CDBBreakPoint::CDBBreakPoint(const BreakpointData &bpd) :
fileName(QDir::toNativeSeparators(bpd.fileName)),
condition(bpd.condition),
ignoreCount(0),
funcName(bpd.funcName),
lineNumber(-1),
oneShot(false),
enabled(bpd.enabled)
{
if (!bpd.ignoreCount.isEmpty())
ignoreCount = bpd.ignoreCount.toInt();
if (!bpd.lineNumber.isEmpty())
lineNumber = bpd.lineNumber.toInt();
}
int CDBBreakPoint::compare(const CDBBreakPoint& rhs) const
{
if (ignoreCount > rhs.ignoreCount)
return 1;
if (ignoreCount < rhs.ignoreCount)
return -1;
if (lineNumber > rhs.lineNumber)
return 1;
if (lineNumber < rhs.lineNumber)
return -1;
if (oneShot && !rhs.oneShot)
return 1;
if (!oneShot && rhs.oneShot)
return -1;
if (enabled && !rhs.enabled)
return 1;
if (!enabled && rhs.enabled)
return -1;
if (const int fileCmp = fileName.compare(rhs.fileName))
return fileCmp;
if (const int funcCmp = funcName.compare(rhs.funcName))
return funcCmp;
if (const int condCmp = condition.compare(rhs.condition))
return condCmp;
return 0;
}
void CDBBreakPoint::clear()
{
ignoreCount = 0;
oneShot = false;
enabled = true;
clearExpressionData();
}
void CDBBreakPoint::clearExpressionData()
{
fileName.clear();
condition.clear();
funcName.clear();
lineNumber = -1;
}
QDebug operator<<(QDebug dbg, const CDBBreakPoint &bp)
{
QDebug nsp = dbg.nospace();
if (!bp.fileName.isEmpty()) {
nsp << "fileName='" << bp.fileName << ':' << bp.lineNumber << '\'';
} else {
nsp << "funcName='" << bp.funcName << '\'';
}
if (!bp.condition.isEmpty())
nsp << " condition='" << bp.condition << '\'';
if (bp.ignoreCount)
nsp << " ignoreCount=" << bp.ignoreCount;
if (bp.enabled)
nsp << " enabled";
if (bp.oneShot)
nsp << " oneShot";
return dbg;
}
QString CDBBreakPoint::expression() const
{
// format the breakpoint expression (file/function and condition)
QString rc;
QTextStream str(&rc);
if (funcName.isEmpty()) {
const QChar sourceFileQuote = QLatin1Char(sourceFileQuoteC);
str << sourceFileQuote << QDir::toNativeSeparators(fileName) << QLatin1Char(':') << lineNumber << sourceFileQuote;
} else {
str << funcName;
}
if (!condition.isEmpty()) {
const QChar doubleQuote = QLatin1Char('"');
str << QLatin1Char(' ') << doubleQuote << condition << doubleQuote;
}
return rc;
}
bool CDBBreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const
{
const QString expr = expression();
if (debugCDB)
qDebug() << Q_FUNC_INFO << *this << expr;
const HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast<PCWSTR>(expr.utf16()));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to set breakpoint '%1' : %2").
arg(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr));
return false;
}
// Pass Count is ignoreCount + 1
ibp->SetPassCount(ignoreCount + 1u);
ULONG flags = 0;
if (enabled)
flags |= DEBUG_BREAKPOINT_ENABLED;
if (oneShot)
flags |= DEBUG_BREAKPOINT_ONE_SHOT;
ibp->AddFlags(flags);
return true;
}
static inline QString msgCannotAddBreakPoint(const QString &why)
{
return QString::fromLatin1("Unable to add breakpoint: %1").arg(why);
}
bool CDBBreakPoint::add(CIDebugControl* debugControl,
QString *errorMessage,
unsigned long *id,
quint64 *address) const
{
IDebugBreakpoint2* ibp = 0;
if (address)
*address = 0;
if (id)
*id = 0;
HRESULT hr = debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, 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;
}
// Helper for normalizing file names:
// Map the device paths in a file name to back to drive letters
// "/Device/HarddiskVolume1/file.cpp" -> "C:/file.cpp"
static bool mapDeviceToDriveLetter(QString *s)
{
enum { bufSize = 512 };
// Retrieve drive letters and get their device names.
// Do not cache as it may change due to removable/network drives.
TCHAR driveLetters[bufSize];
if (!GetLogicalDriveStrings(bufSize-1, driveLetters))
return false;
TCHAR driveName[MAX_PATH];
TCHAR szDrive[3] = TEXT(" :");
for (const TCHAR *driveLetter = driveLetters; *driveLetter; driveLetter++) {
szDrive[0] = *driveLetter; // Look up each device name
if (QueryDosDevice(szDrive, driveName, MAX_PATH)) {
const QString deviceName = QString::fromUtf16(driveName);
if (s->startsWith(deviceName)) {
s->replace(0, deviceName.size(), QString::fromUtf16(szDrive));
return true;
}
}
}
return false;
}
// Helper for normalizing file names:
// Determine normalized case of a Windows file name (camelcase.cpp -> CamelCase.cpp)
// as the debugger reports lower case file names.
// Restriction: File needs to exists and be non-empty and will be to be opened/mapped.
// This is the MSDN-recommended way of doing that. The result should be cached.
static inline QString normalizeFileNameCaseHelper(const QString &f)
{
HANDLE hFile = CreateFile(f.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if(hFile == INVALID_HANDLE_VALUE)
return f;
// Get the file size. We need a non-empty file to map it.
DWORD dwFileSizeHi = 0;
DWORD dwFileSizeLo = GetFileSize(hFile, &dwFileSizeHi);
if (dwFileSizeLo == 0 && dwFileSizeHi == 0) {
CloseHandle(hFile);
return f;
}
// Create a file mapping object.
HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 1, NULL);
if (!hFileMap) {
CloseHandle(hFile);
return f;
}
// Create a file mapping to get the file name.
void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
if (!pMem) {
CloseHandle(hFileMap);
CloseHandle(hFile);
return f;
}
QString rc;
WCHAR pszFilename[MAX_PATH];
pszFilename[0] = 0;
// Get a file name of the form "/Device/HarddiskVolume1/file.cpp"
if (GetMappedFileName (GetCurrentProcess(), pMem, pszFilename, MAX_PATH)) {
rc = QString::fromUtf16(pszFilename);
if (!mapDeviceToDriveLetter(&rc))
rc.clear();
}
UnmapViewOfFile(pMem);
CloseHandle(hFileMap);
CloseHandle(hFile);
return rc.isEmpty() ? f : rc;
}
// 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 CDBBreakPoint::normalizeFileName(const QString &f)
{
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(normalizeFileNameCaseHelper(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;
}
void CDBBreakPoint::clearNormalizeFileNameCache()
{
normalizedFileNameCache()->clear();
}
bool CDBBreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
{
clear();
WCHAR wszBuf[MAX_PATH];
const HRESULT hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint: %1").
arg(CdbCore::msgComFailed("GetOffsetExpressionWide", hr));
return false;
}
// 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 CDBBreakPoint::parseExpression(const QString &expr)
{
clearExpressionData();
const QChar sourceFileQuote = QLatin1Char(sourceFileQuoteC);
// Check for file or function
int conditionPos = 0;
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 CDBBreakPoint::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 CDBBreakPoint::getBreakPoints(CIDebugControl* debugControl, QList<CDBBreakPoint> *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++) {
IDebugBreakpoint2 *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;
}
CDBBreakPoint 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);
}
static IDebugBreakpoint2 *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
static bool 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);
}
static bool 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;
}
static inline QString msgCannotSetBreakAtFunction(const QString &func, const QString &why) static inline QString msgCannotSetBreakAtFunction(const QString &func, const QString &why)
{ {
@@ -512,17 +43,18 @@ static inline QString msgCannotSetBreakAtFunction(const QString &func, const QSt
} }
// Synchronize (halted) engine breakpoints with those of the BreakHandler. // Synchronize (halted) engine breakpoints with those of the BreakHandler.
bool CDBBreakPoint::synchronizeBreakPoints(CIDebugControl* debugControl, bool synchronizeBreakPoints(CIDebugControl* debugControl,
CIDebugSymbols *syms, CIDebugSymbols *syms,
BreakHandler *handler, BreakHandler *handler,
QString *errorMessage, QStringList *warnings) QString *errorMessage,
QStringList *warnings)
{ {
errorMessage->clear(); errorMessage->clear();
warnings->clear(); warnings->clear();
// Do an initial check whether we are in a state that allows // Do an initial check whether we are in a state that allows
// for modifying breakPoints // for modifying breakPoints
ULONG engineCount; ULONG engineCount;
if (!getBreakPointCount(debugControl, &engineCount, errorMessage)) { if (!CdbCore::BreakPoint::getBreakPointCount(debugControl, &engineCount, errorMessage)) {
*errorMessage = QString::fromLatin1("Cannot modify breakpoints: %1").arg(*errorMessage); *errorMessage = QString::fromLatin1("Cannot modify breakpoints: %1").arg(*errorMessage);
return false; return false;
} }
@@ -556,7 +88,7 @@ bool CDBBreakPoint::synchronizeBreakPoints(CIDebugControl* debugControl,
if (breakPointOk) { if (breakPointOk) {
quint64 address; quint64 address;
unsigned long id; unsigned long id;
CDBBreakPoint ncdbbp(*nbd); const CdbCore::BreakPoint ncdbbp = breakPointFromBreakPointData(*nbd);
breakPointOk = ncdbbp.add(debugControl, &warning, &id, &address); breakPointOk = ncdbbp.add(debugControl, &warning, &id, &address);
if (breakPointOk) { if (breakPointOk) {
if (debugBP) if (debugBP)
@@ -578,24 +110,24 @@ bool CDBBreakPoint::synchronizeBreakPoints(CIDebugControl* debugControl,
warnings->push_back(warning); } warnings->push_back(warning); }
// Delete // Delete
foreach (BreakpointData *rbd, handler->takeRemovedBreakpoints()) { foreach (BreakpointData *rbd, handler->takeRemovedBreakpoints()) {
if (!removeBreakPointById(debugControl, rbd->bpNumber.toUInt(), &warning)) if (!CdbCore::BreakPoint::removeBreakPointById(debugControl, rbd->bpNumber.toUInt(), &warning))
warnings->push_back(warning); warnings->push_back(warning);
delete rbd; delete rbd;
} }
// Enable/Disable // Enable/Disable
foreach (BreakpointData *ebd, handler->takeEnabledBreakpoints()) foreach (BreakpointData *ebd, handler->takeEnabledBreakpoints())
if (!setBreakPointEnabledById(debugControl, ebd->bpNumber.toUInt(), true, &warning)) if (!CdbCore::BreakPoint::setBreakPointEnabledById(debugControl, ebd->bpNumber.toUInt(), true, &warning))
warnings->push_back(warning); warnings->push_back(warning);
foreach (BreakpointData *dbd, handler->takeDisabledBreakpoints()) foreach (BreakpointData *dbd, handler->takeDisabledBreakpoints())
if (!setBreakPointEnabledById(debugControl, dbd->bpNumber.toUInt(), false, &warning)) if (!CdbCore::BreakPoint::setBreakPointEnabledById(debugControl, dbd->bpNumber.toUInt(), false, &warning))
warnings->push_back(warning); warnings->push_back(warning);
if (updateMarkers) if (updateMarkers)
handler->updateMarkers(); handler->updateMarkers();
if (debugBP > 1) { if (debugBP > 1) {
QList<CDBBreakPoint> bps; QList<CdbCore::BreakPoint> bps;
CDBBreakPoint::getBreakPoints(debugControl, &bps, errorMessage); CdbCore::BreakPoint::getBreakPoints(debugControl, &bps, errorMessage);
qDebug().nospace() << "### Breakpoints in engine: " << bps; qDebug().nospace() << "### Breakpoints in engine: " << bps;
} }
return true; return true;

View File

@@ -31,73 +31,39 @@
#define CDBBREAKPOINTS_H #define CDBBREAKPOINTS_H
#include "cdbcom.h" #include "cdbcom.h"
#include "breakpoint.h"
#include "breakhandler.h"
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QList> #include <QtCore/QList>
#include <QtCore/QDir>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QDebug; class QDebug;
QT_END_NAMESPACE QT_END_NAMESPACE
// Convert breakpoint structs
inline CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd)
{
CdbCore::BreakPoint rc;
rc.fileName = QDir::toNativeSeparators(bpd.fileName);
rc.condition = bpd.condition;
rc.funcName = bpd.funcName;
rc.ignoreCount = bpd.ignoreCount.isEmpty() ? 0 : bpd.ignoreCount.toInt();
rc.lineNumber = bpd.lineNumber.isEmpty() ? -1 : bpd.lineNumber.toInt();
rc.oneShot = false;
rc.enabled = bpd.enabled;
return rc;
}
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
class BreakHandler; // Synchronize (halted) engine with BreakHandler.
class BreakpointData; bool synchronizeBreakPoints(CIDebugControl* ctl, CIDebugSymbols *syms,
BreakHandler *bh,
QString *errorMessage, QStringList *warnings);
/* CDB Break point data structure with utilities to
* apply to engine and to retrieve them from the engine and comparison. */
struct CDBBreakPoint
{
CDBBreakPoint();
CDBBreakPoint(const BreakpointData &bpd);
int compare(const CDBBreakPoint& rhs) const;
void clear();
void clearExpressionData();
QString expression() const;
// Apply parameters
bool apply(IDebugBreakpoint2 *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(IDebugBreakpoint2 *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<CDBBreakPoint> *bps, QString *errorMessage);
// Synchronize (halted) engine with BreakHandler.
static bool synchronizeBreakPoints(CIDebugControl* ctl, CIDebugSymbols *syms,
BreakHandler *bh,
QString *errorMessage, QStringList *warnings);
// Return a 'canonical' file (using '/' and capitalized drive letter)
static QString normalizeFileName(const QString &f);
static void clearNormalizeFileNameCache();
QString fileName; // short name of source file
QString condition; // condition associated with breakpoint
unsigned long ignoreCount; // ignore count associated with breakpoint
int lineNumber; // line in source file
QString funcName; // name of containing function
bool oneShot;
bool enabled;
};
QDebug operator<<(QDebug, const CDBBreakPoint &bp);
inline bool operator==(const CDBBreakPoint& b1, const CDBBreakPoint& b2)
{ return b1.compare(b2) == 0; }
inline bool operator!=(const CDBBreakPoint& b1, const CDBBreakPoint& b2)
{ return b1.compare(b2) != 0; }
} // namespace Internal } // namespace Internal
} // namespace Debugger } // namespace Debugger

View File

@@ -26,15 +26,24 @@ HEADERS += \
$$PWD/cdbcom.h \ $$PWD/cdbcom.h \
$$PWD/coreengine.h \ $$PWD/coreengine.h \
$$PWD/debugoutputbase.h \ $$PWD/debugoutputbase.h \
$$PWD/debugeventcallbackbase.h $$PWD/debugeventcallbackbase.h \
$$PWD/symbolgroupcontext.h \
$$PWD/stacktracecontext.h \
$$PWD/breakpoint.h
SOURCES += \ SOURCES += \
$$PWD/coreengine.cpp \ $$PWD/coreengine.cpp \
$$PWD/debugoutputbase.cpp \ $$PWD/debugoutputbase.cpp \
$$PWD/debugeventcallbackbase.cpp $$PWD/debugeventcallbackbase.cpp \
$$PWD/symbolgroupcontext.cpp \
$$PWD/stacktracecontext.cpp \
$$PWD/breakpoint.cpp
INCLUDEPATH*=$$PWD INCLUDEPATH*=$$PWD
DEPENDPATH*=$$PWD DEPENDPATH*=$$PWD
LIBS+=-lpsapi
} else { } else {
message("Debugging Tools for Windows could not be found in $$CDB_PATH") message("Debugging Tools for Windows could not be found in $$CDB_PATH")
CDB_PATH="" CDB_PATH=""

View File

@@ -248,17 +248,21 @@ QString CdbDebugEngine::editorToolTip(const QString &exp, const QString &functio
QString errorMessage; QString errorMessage;
QString rc; QString rc;
// Find the frame of the function if there is any // Find the frame of the function if there is any
CdbStackFrameContext *frame = 0; CdbSymbolGroupContext *frame = 0;
if (m_d->m_currentStackTrace && !function.isEmpty()) { if (m_d->m_currentStackTrace && !function.isEmpty()) {
const int frameIndex = m_d->m_currentStackTrace->indexOf(function); const int frameIndex = m_d->m_currentStackTrace->indexOf(function);
if (debugToolTips)
qDebug() << "CdbDebugEngine::editorToolTip" << exp << function << frameIndex;
if (frameIndex != -1) if (frameIndex != -1)
frame = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage); frame = m_d->m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, &errorMessage);
} }
if (frame && frame->editorToolTip(QLatin1String("local.") + exp, &rc, &errorMessage)) if (frame && frame->editorToolTip(QLatin1String("local.") + exp, &rc, &errorMessage))
return rc; return rc;
// No function/symbol context found, try to evaluate in current context. // No function/symbol context found, try to evaluate in current context.
// Do not append type as this will mostly be 'long long' for integers, etc. // Do not append type as this will mostly be 'long long' for integers, etc.
QString type; QString type;
if (debugToolTips)
qDebug() << "Defaulting to expression";
if (!m_d->evaluateExpression(exp, &rc, &type, &errorMessage)) if (!m_d->evaluateExpression(exp, &rc, &type, &errorMessage))
return QString(); return QString();
return rc; return rc;
@@ -341,7 +345,7 @@ void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters>
{ {
if (debugCDBExecution) if (debugCDBExecution)
qDebug() << "startDebugger" << *sp; qDebug() << "startDebugger" << *sp;
CDBBreakPoint::clearNormalizeFileNameCache(); CdbCore::BreakPoint::clearNormalizeFileNameCache();
setState(AdapterStarting, Q_FUNC_INFO, __LINE__); setState(AdapterStarting, Q_FUNC_INFO, __LINE__);
m_d->checkVersion(); m_d->checkVersion();
if (m_d->m_hDebuggeeProcess) { if (m_d->m_hDebuggeeProcess) {
@@ -599,13 +603,13 @@ void CdbDebugEngine::detachDebugger()
m_d->endDebugging(CdbDebugEnginePrivate::EndDebuggingDetach); m_d->endDebugging(CdbDebugEnginePrivate::EndDebuggingDetach);
} }
CdbStackFrameContext *CdbDebugEnginePrivate::getStackFrameContext(int frameIndex, QString *errorMessage) const CdbSymbolGroupContext *CdbDebugEnginePrivate::getSymbolGroupContext(int frameIndex, QString *errorMessage) const
{ {
if (!m_currentStackTrace) { if (!m_currentStackTrace) {
*errorMessage = QLatin1String(msgNoStackTraceC); *errorMessage = QLatin1String(msgNoStackTraceC);
return 0; return 0;
} }
if (CdbStackFrameContext *sg = m_currentStackTrace->frameContextAt(frameIndex, errorMessage)) if (CdbSymbolGroupContext *sg = m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, errorMessage))
return sg; return sg;
return 0; return 0;
} }
@@ -649,7 +653,7 @@ void CdbDebugEngine::updateWatchData(const WatchData &incomplete)
bool success = false; bool success = false;
QString errorMessage; QString errorMessage;
do { do {
CdbStackFrameContext *sg = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage); CdbSymbolGroupContext *sg = m_d->m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, &errorMessage);
if (!sg) if (!sg)
break; break;
if (!sg->completeData(incomplete, watchHandler, &errorMessage)) if (!sg->completeData(incomplete, watchHandler, &errorMessage))
@@ -902,7 +906,7 @@ void CdbDebugEngine::runToLineExec(const QString &fileName, int lineNumber)
{ {
manager()->showDebuggerOutput(LogMisc, tr("Running up to %1:%2...").arg(fileName).arg(lineNumber)); manager()->showDebuggerOutput(LogMisc, tr("Running up to %1:%2...").arg(fileName).arg(lineNumber));
QString errorMessage; QString errorMessage;
CDBBreakPoint tempBreakPoint; CdbCore::BreakPoint tempBreakPoint;
tempBreakPoint.fileName = fileName; tempBreakPoint.fileName = fileName;
tempBreakPoint.lineNumber = lineNumber; tempBreakPoint.lineNumber = lineNumber;
tempBreakPoint.oneShot = true; tempBreakPoint.oneShot = true;
@@ -916,7 +920,7 @@ void CdbDebugEngine::runToFunctionExec(const QString &functionName)
{ {
manager()->showDebuggerOutput(LogMisc, tr("Running up to function '%1()'...").arg(functionName)); manager()->showDebuggerOutput(LogMisc, tr("Running up to function '%1()'...").arg(functionName));
QString errorMessage; QString errorMessage;
CDBBreakPoint tempBreakPoint; CdbCore::BreakPoint tempBreakPoint;
tempBreakPoint.funcName = functionName; tempBreakPoint.funcName = functionName;
tempBreakPoint.oneShot = true; tempBreakPoint.oneShot = true;
const bool ok = tempBreakPoint.add(m_d->interfaces().debugControl, &errorMessage) const bool ok = tempBreakPoint.add(m_d->interfaces().debugControl, &errorMessage)
@@ -939,7 +943,7 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v
bool success = false; bool success = false;
do { do {
QString newValue; QString newValue;
CdbStackFrameContext *sg = m_d->getStackFrameContext(frameIndex, &errorMessage); CdbSymbolGroupContext *sg = m_d->getSymbolGroupContext(frameIndex, &errorMessage);
if (!sg) if (!sg)
break; break;
if (!sg->assignValue(expr, value, &newValue, &errorMessage)) if (!sg->assignValue(expr, value, &newValue, &errorMessage))
@@ -1004,7 +1008,7 @@ void CdbDebugEngine::activateFrame(int frameIndex)
if (oldIndex != frameIndex || m_d->m_firstActivatedFrame) { if (oldIndex != frameIndex || m_d->m_firstActivatedFrame) {
watchHandler->beginCycle(); watchHandler->beginCycle();
if (CdbStackFrameContext *sgc = m_d->getStackFrameContext(frameIndex, &errorMessage)) if (CdbSymbolGroupContext *sgc = m_d->getSymbolGroupContext(frameIndex, &errorMessage))
success = sgc->populateModelInitially(watchHandler, &errorMessage); success = sgc->populateModelInitially(watchHandler, &errorMessage);
watchHandler->endCycle(); watchHandler->endCycle();
} else { } else {
@@ -1059,7 +1063,7 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa
// called again from the debug event handler. // called again from the debug event handler.
ULONG dummy; ULONG dummy;
const bool wasRunning = !CDBBreakPoint::getBreakPointCount(interfaces().debugControl, &dummy); const bool wasRunning = !CdbCore::BreakPoint::getBreakPointCount(interfaces().debugControl, &dummy);
if (debugCDB) if (debugCDB)
qDebug() << Q_FUNC_INFO << "\n Running=" << wasRunning; qDebug() << Q_FUNC_INFO << "\n Running=" << wasRunning;
@@ -1074,10 +1078,10 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa
} }
QStringList warnings; QStringList warnings;
const bool ok = CDBBreakPoint::synchronizeBreakPoints(interfaces().debugControl, const bool ok = synchronizeBreakPoints(interfaces().debugControl,
interfaces().debugSymbols, interfaces().debugSymbols,
manager()->breakHandler(), manager()->breakHandler(),
errorMessage, &warnings); errorMessage, &warnings);
if (const int warningsCount = warnings.size()) if (const int warningsCount = warnings.size())
for (int w = 0; w < warningsCount; w++) for (int w = 0; w < warningsCount; w++)
m_engine->warning(warnings.at(w)); m_engine->warning(warnings.at(w));
@@ -1361,7 +1365,7 @@ ULONG CdbDebugEnginePrivate::updateThreadList()
ULONG currentThreadId; ULONG currentThreadId;
QString errorMessage; QString errorMessage;
// When interrupting, an artifical thread with a breakpoint is created. // When interrupting, an artifical thread with a breakpoint is created.
if (!CdbStackTraceContext::getThreads(interfaces(), true, &threads, &currentThreadId, &errorMessage)) if (!CdbStackTraceContext::getThreads(interfaces(), &threads, &currentThreadId, &errorMessage))
m_engine->warning(errorMessage); m_engine->warning(errorMessage);
manager()->threadsHandler()->setThreads(threads); manager()->threadsHandler()->setThreads(threads);
return currentThreadId; return currentThreadId;
@@ -1378,14 +1382,18 @@ static inline unsigned long dumperThreadId(const QList<StackFrame> &frames,
{ {
if (frames.empty()) if (frames.empty())
return CdbDumperHelper::InvalidDumperCallThread; return CdbDumperHelper::InvalidDumperCallThread;
if (frames.at(0).function == QLatin1String(CdbStackTraceContext::winFuncDebugBreakPoint)) switch (CdbCore::StackTraceContext::specialFunction(frames.at(0).from, frames.at(0).function)) {
case CdbCore::StackTraceContext::BreakPointFunction:
case CdbCore::StackTraceContext::WaitFunction:
return CdbDumperHelper::InvalidDumperCallThread; return CdbDumperHelper::InvalidDumperCallThread;
default:
break;
}
// Check remaining frames for wait
const int waitCheckDepth = qMin(frames.size(), 5); const int waitCheckDepth = qMin(frames.size(), 5);
static const QString waitForPrefix = QLatin1String(CdbStackTraceContext::winFuncWaitForPrefix); for (int f = 1; f < waitCheckDepth; f++) {
static const QString msgWaitForPrefix = QLatin1String(CdbStackTraceContext::winFuncMsgWaitForPrefix); if (CdbCore::StackTraceContext::specialFunction(frames.at(f).from, frames.at(f).function)
for (int f = 0; f < waitCheckDepth; f++) { == CdbCore::StackTraceContext::WaitFunction)
const QString &function = frames.at(f).function;
if (function.startsWith(waitForPrefix) || function.startsWith(msgWaitForPrefix))
return CdbDumperHelper::InvalidDumperCallThread; return CdbDumperHelper::InvalidDumperCallThread;
} }
return currentThread; return currentThread;
@@ -1404,7 +1412,7 @@ void CdbDebugEnginePrivate::updateStackTrace()
return; return;
} }
m_currentStackTrace = m_currentStackTrace =
CdbStackTraceContext::create(m_dumper, m_currentThreadId, &errorMessage); CdbStackTraceContext::create(m_dumper, &errorMessage);
if (!m_currentStackTrace) { if (!m_currentStackTrace) {
m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage)); m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
return; return;
@@ -1413,7 +1421,7 @@ void CdbDebugEnginePrivate::updateStackTrace()
#if 0 #if 0
m_engine->reloadDisassembler(); // requires stack trace m_engine->reloadDisassembler(); // requires stack trace
#endif #endif
const QList<StackFrame> stackFrames = m_currentStackTrace->frames(); const QList<StackFrame> stackFrames = m_currentStackTrace->stackFrames();
// find the first usable frame and select it // find the first usable frame and select it
int current = -1; int current = -1;
const int count = stackFrames.count(); const int count = stackFrames.count();

View File

@@ -46,8 +46,8 @@ class DebuggerManager;
namespace Internal { namespace Internal {
class WatchHandler; class WatchHandler;
class CdbStackFrameContext;
class CdbStackTraceContext; class CdbStackTraceContext;
class CdbSymbolGroupContext;
class CdbDebugEnginePrivate : public CdbCore::CoreEngine class CdbDebugEnginePrivate : public CdbCore::CoreEngine
{ {
@@ -82,7 +82,7 @@ public:
void cleanStackTrace(); void cleanStackTrace();
void clearForRun(); void clearForRun();
void handleModuleLoad(const QString &); void handleModuleLoad(const QString &);
CdbStackFrameContext *getStackFrameContext(int frameIndex, QString *errorMessage) const; CdbSymbolGroupContext *getSymbolGroupContext(int frameIndex, QString *errorMessage) const;
void clearDisplay(); void clearDisplay();
bool interruptInterferiorProcess(QString *errorMessage); bool interruptInterferiorProcess(QString *errorMessage);
@@ -135,6 +135,7 @@ enum { messageTimeOut = 5000 };
enum { debugCDB = 0 }; enum { debugCDB = 0 };
enum { debugCDBExecution = 0 }; enum { debugCDBExecution = 0 };
enum { debugCDBWatchHandling = 0 }; enum { debugCDBWatchHandling = 0 };
enum { debugToolTips = 0 };
} // namespace Internal } // namespace Internal
} // namespace Debugger } // namespace Debugger

View File

@@ -75,7 +75,7 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
QString msg; QString msg;
{ {
QTextStream str(&msg); QTextStream str(&msg);
formatException(Exception, m_pEngine->m_d->m_dumper, str); formatException(Exception, &m_pEngine->m_d->interfaces(), str);
} }
const bool fatal = isFatalException(Exception->ExceptionCode); const bool fatal = isFatalException(Exception->ExceptionCode);
if (debugCDB) if (debugCDB)

View File

@@ -29,8 +29,7 @@
#include "cdbexceptionutils.h" #include "cdbexceptionutils.h"
#include "cdbdebugengine_p.h" #include "cdbdebugengine_p.h"
#include "cdbdumperhelper.h" #include "stacktracecontext.h"
#include "cdbstacktracecontext.h"
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QTextStream> #include <QtCore/QTextStream>
@@ -247,15 +246,13 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
// Format exception with stacktrace in case of C++ exception // Format exception with stacktrace in case of C++ exception
void formatException(const EXCEPTION_RECORD64 *e, void formatException(const EXCEPTION_RECORD64 *e,
const QSharedPointer<CdbDumperHelper> &dumper, const CdbCore::ComInterfaces *cif,
QTextStream &str) QTextStream &str)
{ {
formatException(e, str); formatException(e, str);
if (e->ExceptionCode == winExceptionCppException) { if (e->ExceptionCode == winExceptionCppException) {
QString errorMessage; QString errorMessage;
ULONG currentThreadId = 0; if (CdbCore::StackTraceContext *stc = CdbCore::StackTraceContext::create(cif, 9999, &errorMessage)) {
dumper->comInterfaces()->debugSystemObjects->GetCurrentThreadId(&currentThreadId);
if (CdbStackTraceContext *stc = CdbStackTraceContext::create(dumper, currentThreadId, &errorMessage)) {
str << "at:\n"; str << "at:\n";
stc->format(str); stc->format(str);
str <<'\n'; str <<'\n';

View File

@@ -39,6 +39,10 @@ QT_BEGIN_NAMESPACE
class QTextStream; class QTextStream;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace CdbCore {
struct ComInterfaces;
}
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
@@ -94,7 +98,7 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str);
// Format exception with stacktrace in case of C++ exception // Format exception with stacktrace in case of C++ exception
void formatException(const EXCEPTION_RECORD64 *e, void formatException(const EXCEPTION_RECORD64 *e,
const QSharedPointer<CdbDumperHelper> &dumper, const CdbCore::ComInterfaces *cif,
QTextStream &str); QTextStream &str);
// Is this a crash/recoverable? // Is this a crash/recoverable?

View File

@@ -310,15 +310,14 @@ bool CdbStackFrameContext::populateModelInitially(WatchHandler *wh, QString *err
qDebug() << "populateModelInitially dumpers=" << m_useDumpers; qDebug() << "populateModelInitially dumpers=" << m_useDumpers;
// Recurse down items that are initially expanded in the view, stop processing for // Recurse down items that are initially expanded in the view, stop processing for
// dumper items. // dumper items.
const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper, const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper);
m_dumper->comInterfaces()->debugDataSpaces);
const bool rc = m_useDumpers ? const bool rc = m_useDumpers ?
CdbSymbolGroupContext::populateModelInitially(rctx, CdbSymbolGroupContext::populateModelInitiallyHelper(rctx,
WatchHandleDumperInserter(wh, m_dumper), WatchHandleDumperInserter(wh, m_dumper),
WatchHandlerExpandedPredicate(wh), WatchHandlerExpandedPredicate(wh),
isDumperPredicate, isDumperPredicate,
errorMessage) : errorMessage) :
CdbSymbolGroupContext::populateModelInitially(rctx, CdbSymbolGroupContext::populateModelInitiallyHelper(rctx,
WatchHandlerModelInserter(wh), WatchHandlerModelInserter(wh),
WatchHandlerExpandedPredicate(wh), WatchHandlerExpandedPredicate(wh),
falsePredicate, falsePredicate,
@@ -333,11 +332,10 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal,
if (debugCDBWatchHandling) if (debugCDBWatchHandling)
qDebug() << ">completeData src=" << incompleteLocal.source << incompleteLocal.toString(); qDebug() << ">completeData src=" << incompleteLocal.source << incompleteLocal.toString();
const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper, const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper);
m_dumper->comInterfaces()->debugDataSpaces);
// Expand symbol group items, recurse one level from desired item // Expand symbol group items, recurse one level from desired item
if (!m_useDumpers) { if (!m_useDumpers) {
return CdbSymbolGroupContext::completeData(rctx, incompleteLocal, return CdbSymbolGroupContext::completeDataHelper(rctx, incompleteLocal,
WatchHandlerModelInserter(wh), WatchHandlerModelInserter(wh),
MatchINamePredicate(incompleteLocal.iname), MatchINamePredicate(incompleteLocal.iname),
falsePredicate, falsePredicate,
@@ -375,7 +373,7 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal,
} }
// Expand symbol group items, recurse one level from desired item // Expand symbol group items, recurse one level from desired item
return CdbSymbolGroupContext::completeData(rctx, incompleteLocal, return CdbSymbolGroupContext::completeDataHelper(rctx, incompleteLocal,
WatchHandleDumperInserter(wh, m_dumper), WatchHandleDumperInserter(wh, m_dumper),
MatchINamePredicate(incompleteLocal.iname), MatchINamePredicate(incompleteLocal.iname),
isDumperPredicate, isDumperPredicate,
@@ -398,8 +396,12 @@ bool CdbStackFrameContext::editorToolTip(const QString &iname,
return false; return false;
} }
// Check dumpers. Should actually be just one item. // Check dumpers. Should actually be just one item.
const WatchData wd = m_symbolContext->watchDataAt(index);
if (m_useDumpers && !wd.error && m_dumper->state() != CdbDumperHelper::Disabled) { 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; QList<WatchData> result;
if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, &result, errorMessage)) { if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, &result, errorMessage)) {
foreach (const WatchData &dwd, result) { foreach (const WatchData &dwd, result) {

View File

@@ -1,78 +0,0 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef CDBSTACKFRAMECONTEXT_H
#define CDBSTACKFRAMECONTEXT_H
#include <QtCore/QList>
#include <QtCore/QSharedPointer>
namespace Debugger {
namespace Internal {
class WatchData;
class WatchHandler;
class CdbSymbolGroupContext;
class CdbDumperHelper;
/* CdbStackFrameContext 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 CdbStackFrameContext
{
Q_DISABLE_COPY(CdbStackFrameContext)
public:
// Mask bits for the source field of watch data.
enum { SourceMask = 0xFF, ChildrenKnownBit = 0x0100 };
explicit CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper,
CdbSymbolGroupContext *symbolContext);
~CdbStackFrameContext();
bool assignValue(const QString &iname, const QString &value,
QString *newValue /* = 0 */, 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:
const bool m_useDumpers;
const QSharedPointer<CdbDumperHelper> m_dumper;
CdbSymbolGroupContext *m_symbolContext;
};
} // namespace Internal
} // namespace Debugger
#endif // CDBSTACKFRAMECONTEXT_H

View File

@@ -28,332 +28,110 @@
**************************************************************************/ **************************************************************************/
#include "cdbstacktracecontext.h" #include "cdbstacktracecontext.h"
#include "coreengine.h"
#include "cdbstackframecontext.h"
#include "cdbbreakpoint.h"
#include "cdbsymbolgroupcontext.h" #include "cdbsymbolgroupcontext.h"
#include "cdbdebugengine_p.h"
#include "cdbdumperhelper.h" #include "cdbdumperhelper.h"
#include "cdbdebugengine_p.h"
#include "debuggeractions.h" #include "debuggeractions.h"
#include "debuggermanager.h" #include "watchutils.h"
#include <QtCore/QDir>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QTextStream>
enum { debug = 1 };
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
const char *CdbStackTraceContext::winFuncFastSystemCallRet = "ntdll!KiFastSystemCallRet";
const char *CdbStackTraceContext::winFuncDebugBreakPoint = "ntdll!DbgBreakPoint";
const char *CdbStackTraceContext::winFuncWaitForPrefix = "kernel32!WaitFor";
const char *CdbStackTraceContext::winFuncMsgWaitForPrefix = "kernel32!MsgWaitForMultipleObjects";
CdbStackTraceContext::CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper) : CdbStackTraceContext::CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper) :
m_dumper(dumper), CdbCore::StackTraceContext(dumper->comInterfaces()),
m_cif(dumper->comInterfaces()), m_dumper(dumper)
m_instructionOffset(0)
{ {
} }
CdbStackTraceContext *CdbStackTraceContext::create(const QSharedPointer<CdbDumperHelper> &dumper, CdbStackTraceContext *CdbStackTraceContext::create(const QSharedPointer<CdbDumperHelper> &dumper,
unsigned long threadId,
QString *errorMessage) QString *errorMessage)
{ {
if (debugCDB)
qDebug() << Q_FUNC_INFO << threadId;
// fill the DEBUG_STACK_FRAME array
ULONG frameCount;
CdbStackTraceContext *ctx = new CdbStackTraceContext(dumper); CdbStackTraceContext *ctx = new CdbStackTraceContext(dumper);
const HRESULT hr = dumper->comInterfaces()->debugControl->GetStackTrace(0, 0, 0, ctx->m_cdbFrames, CdbStackTraceContext::maxFrames, &frameCount); if (!ctx->init(UINT_MAX, errorMessage)) {
if (FAILED(hr)) {
delete ctx;
*errorMessage = CdbCore::msgComFailed("GetStackTrace", hr);
return 0;
}
if (!ctx->init(frameCount, errorMessage)) {
delete ctx; delete ctx;
return 0; return 0;
} }
return ctx; return ctx;
} }
CdbStackTraceContext::~CdbStackTraceContext() CdbCore::SymbolGroupContext
*CdbStackTraceContext::createSymbolGroup(const CdbCore::ComInterfaces & /* cif */,
int index,
const QString &prefix,
CIDebugSymbolGroup *comSymbolGroup,
QString *errorMessage)
{ {
qDeleteAll(m_frameContexts);
}
bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessage*/)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << frameCount;
const QChar exclamationMark = QLatin1Char('!');
m_frameContexts.resize(frameCount);
qFill(m_frameContexts, static_cast<CdbStackFrameContext*>(0));
// Convert the DEBUG_STACK_FRAMEs to our StackFrame structure and populate the frames
WCHAR wszBuf[MAX_PATH];
for (ULONG i=0; i < frameCount; ++i) {
StackFrame frame;
frame.level = i;
const ULONG64 instructionOffset = m_cdbFrames[i].InstructionOffset;
if (i == 0)
m_instructionOffset = instructionOffset;
frame.address = QString::fromLatin1("0x%1").arg(instructionOffset, 0, 16);
m_cif->debugSymbols->GetNameByOffsetWide(instructionOffset, wszBuf, MAX_PATH, 0, 0);
// Determine function and module, if available
frame.function = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
const int moduleSepPos = frame.function.indexOf(exclamationMark);
if (moduleSepPos != -1)
frame.from = frame.function.mid(0, moduleSepPos);
ULONG ulLine;
ULONG64 ul64Displacement;
const HRESULT hr = m_cif->debugSymbols->GetLineByOffsetWide(instructionOffset, &ulLine, wszBuf, MAX_PATH, 0, &ul64Displacement);
if (SUCCEEDED(hr)) {
frame.line = ulLine;
// Vitally important to use canonical file that matches editormanager,
// else the marker will not show.
frame.file = CDBBreakPoint::normalizeFileName(QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)));
}
m_frames.push_back(frame);
}
return true;
}
int CdbStackTraceContext::indexOf(const QString &function) const
{
const QChar exclamationMark = QLatin1Char('!');
const int count = m_frames.size();
// Module contained ('module!foo'). Exact match
if (function.contains(exclamationMark)) {
for (int i = 0; i < count; i++)
if (m_frames.at(i).function == function)
return i;
return -1;
}
// No module, fuzzy match
QString pattern = exclamationMark + function;
for (int i = 0; i < count; i++)
if (m_frames.at(i).function.endsWith(pattern))
return i;
return -1;
}
static inline QString msgFrameContextFailed(int index, const StackFrame &f, const QString &why)
{
return QString::fromLatin1("Unable to create stack frame context #%1, %2:%3 (%4): %5").
arg(index).arg(f.function).arg(f.line).arg(f.file, why);
}
CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *errorMessage)
{
// Create a frame on demand
if (debugCDB)
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))
return m_frameContexts.at(index);
CIDebugSymbolGroup *sg = createSymbolGroup(index, errorMessage);
if (!sg) {
*errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage);
return 0;
}
// Exclude uninitialized variables if desired // Exclude uninitialized variables if desired
QStringList uninitializedVariables; QStringList uninitializedVariables;
if (theDebuggerAction(UseCodeModel)->isChecked()) { const CdbCore::StackFrame &frame = stackFrameAt(index);
const StackFrame &frame = m_frames.at(index); if (theDebuggerAction(UseCodeModel)->isChecked())
getUninitializedVariables(DebuggerManager::instance()->cppCodeModelSnapshot(), frame.function, frame.file, frame.line, &uninitializedVariables); getUninitializedVariables(DebuggerManager::instance()->cppCodeModelSnapshot(), frame.function, frame.fileName, frame.line, &uninitializedVariables);
} if (debug)
CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(QLatin1String("local"), sg, uninitializedVariables, errorMessage); qDebug() << frame << uninitializedVariables;
CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(prefix,
comSymbolGroup,
m_dumper,
uninitializedVariables,
errorMessage);
if (!sc) { if (!sc) {
*errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage); *errorMessage = msgFrameContextFailed(index, frame, *errorMessage);
return 0; return 0;
} }
CdbStackFrameContext *fr = new CdbStackFrameContext(m_dumper, sc); return sc;
m_frameContexts[index] = fr;
return fr;
} }
CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *errorMessage) CdbSymbolGroupContext *CdbStackTraceContext::cdbSymbolGroupContextAt(int index, QString *errorMessage)
{ {
CIDebugSymbolGroup *sg = 0; return static_cast<CdbSymbolGroupContext *>(symbolGroupContextAt(index, errorMessage));
HRESULT hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &sg);
if (FAILED(hr)) {
*errorMessage = CdbCore::msgComFailed("GetScopeSymbolGroup", hr);
return 0;
}
hr = m_cif->debugSymbols->SetScope(0, m_cdbFrames + index, NULL, 0);
if (FAILED(hr)) {
*errorMessage = CdbCore::msgComFailed("SetScope", hr);
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;
}
return sg;
} }
QString CdbStackTraceContext::toString() const QList<StackFrame> CdbStackTraceContext::stackFrames() const
{ {
QString rc; // Convert from Core data structures
QTextStream str(&rc); QList<StackFrame> rc;
format(str); const int count = frameCount();
const QString hexPrefix = QLatin1String("0x");
for(int i = 0; i < count; i++) {
const CdbCore::StackFrame &coreFrame = stackFrameAt(i);
StackFrame frame;
frame.level = i;
frame.file = coreFrame.fileName;
frame.line = coreFrame.line;
frame.function =coreFrame.function;
frame.from = coreFrame.module;
frame.address = hexPrefix + QString::number(coreFrame.address, 16);
rc.push_back(frame);
}
return rc; return rc;
} }
void CdbStackTraceContext::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++) {
const StackFrame &frame = m_frames.at(f);
const bool hasFile = !frame.file.isEmpty();
// left-pad level
str << qSetFieldWidth(6) << left << f;
str.setFieldWidth(defaultFieldWidth);
str.setFieldAlignment(defaultAlignment);
if (hasFile)
str << QDir::toNativeSeparators(frame.file) << ':' << frame.line << " (";
str << frame.function;
if (hasFile)
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.
static inline bool getStoppedThreadState(const CdbCore::ComInterfaces &cif,
ThreadData *t,
QString *errorMessage)
{
enum { MaxFrames = 2 };
ULONG currentThread;
HRESULT hr = cif.debugSystemObjects->GetCurrentThreadId(&currentThread);
if (FAILED(hr)) {
*errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("GetCurrentThreadId", hr));
return false;
}
if (currentThread != t->id) {
hr = cif.debugSystemObjects->SetCurrentThreadId(t->id);
if (FAILED(hr)) {
*errorMessage = msgGetThreadStateFailed(t->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(t->id, CdbCore::msgComFailed("GetStackTrace", hr));
return false;
}
// Ignore the top frame if it is "ntdll!KiFastSystemCallRet", which is
// not interesting for display.
WCHAR wszBuf[MAX_PATH];
for (int frame = 0; frame < MaxFrames; frame++) {
cif.debugSymbols->GetNameByOffsetWide(frames[frame].InstructionOffset, wszBuf, MAX_PATH, 0, 0);
t->function = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
if (frame != 0 || t->function != QLatin1String(CdbStackTraceContext::winFuncFastSystemCallRet)) {
t->address = frames[frame].InstructionOffset;
cif.debugSymbols->GetNameByOffsetWide(frames[frame].InstructionOffset, wszBuf, MAX_PATH, 0, 0);
t->function = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
ULONG ulLine;
hr = cif.debugSymbols->GetLineByOffsetWide(frames[frame].InstructionOffset, &ulLine, wszBuf, MAX_PATH, 0, 0);
if (SUCCEEDED(hr)) {
t->line = ulLine;
// Just display base name
t->file = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
if (!t->file.isEmpty()) {
const int slashPos = t->file.lastIndexOf(QLatin1Char('\\'));
if (slashPos != -1)
t->file.remove(0, slashPos + 1);
}
}
break;
} // was not "ntdll!KiFastSystemCallRet"
}
return true;
}
static inline QString msgGetThreadsFailed(const QString &why)
{
return QString::fromLatin1("Unable to determine the thread information: %1").arg(why);
}
bool CdbStackTraceContext::getThreads(const CdbCore::ComInterfaces &cif, bool CdbStackTraceContext::getThreads(const CdbCore::ComInterfaces &cif,
bool isStopped,
QList<ThreadData> *threads, QList<ThreadData> *threads,
ULONG *currentThreadId, ULONG *currentThreadId,
QString *errorMessage) QString *errorMessage)
{ {
// Convert from Core data structures
threads->clear(); threads->clear();
ULONG threadCount; ThreadIdFrameMap threadMap;
*currentThreadId = 0; if (!CdbCore::StackTraceContext::getThreads(cif, &threadMap,
HRESULT hr= cif.debugSystemObjects->GetNumberThreads(&threadCount); currentThreadId, errorMessage))
if (FAILED(hr)) {
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetNumberThreads", hr));
return false; return false;
} const QChar slash = QLatin1Char('/');
// Get ids and index of current const ThreadIdFrameMap::const_iterator cend = threadMap.constEnd();
if (!threadCount) for (ThreadIdFrameMap::const_iterator it = threadMap.constBegin(); it != cend; ++it) {
return true; ThreadData data(it.key());
hr = cif.debugSystemObjects->GetCurrentThreadId(currentThreadId); const CdbCore::StackFrame &coreFrame = it.value();
if (FAILED(hr)) { data.address = coreFrame.address;
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetCurrentThreadId", hr)); data.function = coreFrame.function;
return false; data.line = coreFrame.line;
} // Basename only for brevity
const int slashPos = coreFrame.fileName.lastIndexOf(slash);
QVector<ULONG> threadIds(threadCount); data.file = slashPos == -1 ? coreFrame.fileName : coreFrame.fileName.mid(slashPos + 1);
hr = cif.debugSystemObjects->GetThreadIdsByIndex(0, threadCount, &(*threadIds.begin()), 0); threads->push_back(data);
if (FAILED(hr)) {
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetThreadIdsByIndex", hr));
return false;
}
for (ULONG i = 0; i < threadCount; i++) {
ThreadData threadData(threadIds.at(i));
if (isStopped) {
if (!getStoppedThreadState(cif, &threadData, errorMessage)) {
qWarning("%s\n", qPrintable(*errorMessage));
errorMessage->clear();
}
}
threads->push_back(threadData);
}
// Restore thread id
if (isStopped && threads->back().id != *currentThreadId) {
hr = cif.debugSystemObjects->SetCurrentThreadId(*currentThreadId);
if (FAILED(hr)) {
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("SetCurrentThreadId", hr));
return false;
}
} }
return true; return true;
} }

View File

@@ -31,6 +31,7 @@
#define CDBSTACKTRACECONTEXT_H #define CDBSTACKTRACECONTEXT_H
#include "stackhandler.h" #include "stackhandler.h"
#include "stacktracecontext.h"
#include "cdbcom.h" #include "cdbcom.h"
@@ -54,65 +55,38 @@ class CdbStackFrameContext;
class CdbDumperHelper; class CdbDumperHelper;
struct ThreadData; struct ThreadData;
/* Context representing a break point stack consisting of several frames. /* CdbStackTraceContext: Bridges CdbCore data structures and
* Maintains an on-demand constructed list of CdbStackFrameContext * Debugger structures and handles CdbSymbolGroupContext's. */
* containining the local variables of the stack. */
class CdbStackTraceContext class CdbStackTraceContext : public CdbCore::StackTraceContext
{ {
Q_DISABLE_COPY(CdbStackTraceContext) Q_DISABLE_COPY(CdbStackTraceContext)
explicit CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper); explicit CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper);
public: public:
enum { maxFrames = 100 };
// Some well known-functions
static const char *winFuncFastSystemCallRet;
// WaitFor...
static const char *winFuncWaitForPrefix;
static const char *winFuncMsgWaitForPrefix;
// Dummy function used for interrupting a debuggee
static const char *winFuncDebugBreakPoint;
~CdbStackTraceContext();
static CdbStackTraceContext *create(const QSharedPointer<CdbDumperHelper> &dumper, static CdbStackTraceContext *create(const QSharedPointer<CdbDumperHelper> &dumper,
unsigned long threadid,
QString *errorMessage); QString *errorMessage);
QList<StackFrame> frames() const { return m_frames; } CdbSymbolGroupContext *cdbSymbolGroupContextAt(int index, QString *errorMessage);
inline int frameCount() const { return m_frames.size(); }
// Search for function. Should ideally contain the module as 'module!foo'.
int indexOf(const QString &function) const;
// Top-Level instruction offset for disassembler QList<StackFrame> stackFrames() const;
ULONG64 instructionOffset() const { return m_instructionOffset; }
CdbStackFrameContext *frameContextAt(int index, QString *errorMessage); // get threads in stopped state
// Format for logging
void format(QTextStream &str) const;
QString toString() const;
// Retrieve information about threads. When stopped, add
// current stack frame.
static bool getThreads(const CdbCore::ComInterfaces &cif, static bool getThreads(const CdbCore::ComInterfaces &cif,
bool isStopped,
QList<ThreadData> *threads, QList<ThreadData> *threads,
ULONG *currentThreadId, ULONG *currentThreadId,
QString *errorMessage); QString *errorMessage);
protected:
virtual CdbCore::SymbolGroupContext *createSymbolGroup(const CdbCore::ComInterfaces &cif,
int index,
const QString &prefix,
CIDebugSymbolGroup *comSymbolGroup,
QString *errorMessage);
private: private:
bool init(unsigned long frameCount, QString *errorMessage);
CIDebugSymbolGroup *createSymbolGroup(int index, QString *errorMessage);
const QSharedPointer<CdbDumperHelper> m_dumper; const QSharedPointer<CdbDumperHelper> m_dumper;
const CdbCore::ComInterfaces *m_cif;
DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
QVector <CdbStackFrameContext*> m_frameContexts;
QList<StackFrame> m_frames;
ULONG64 m_instructionOffset;
}; };
} // namespace Internal } // namespace Internal

File diff suppressed because it is too large Load Diff

View File

@@ -31,12 +31,13 @@
#define CDBSYMBOLGROUPCONTEXT_H #define CDBSYMBOLGROUPCONTEXT_H
#include "cdbcom.h" #include "cdbcom.h"
#include "watchhandler.h" #include "symbolgroupcontext.h"
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QVector> #include <QtCore/QVector>
#include <QtCore/QList> #include <QtCore/QList>
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QtCore/QSharedPointer>
#include <QtCore/QPair> #include <QtCore/QPair>
#include <QtCore/QMap> #include <QtCore/QMap>
#include <QtCore/QSet> #include <QtCore/QSet>
@@ -47,50 +48,47 @@ namespace Internal {
class WatchData; class WatchData;
class WatchHandler; class WatchHandler;
struct CdbSymbolGroupRecursionContext; struct CdbSymbolGroupRecursionContext;
class CdbDumperHelper;
/* 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 CdbSymbolGroupContext /* 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); Q_DISABLE_COPY(CdbSymbolGroupContext);
explicit CdbSymbolGroupContext(const QString &prefix, explicit CdbSymbolGroupContext(const QString &prefix,
CIDebugSymbolGroup *symbolGroup, CIDebugSymbolGroup *symbolGroup,
const QStringList &uninitializedVariables = QStringList()); const QSharedPointer<CdbDumperHelper> &dumper,
const QStringList &uninitializedVariables);
public: public:
~CdbSymbolGroupContext(); // Mask bits for the source field of watch data.
static CdbSymbolGroupContext *create(const QString &prefix, enum { SourceMask = 0xFF, ChildrenKnownBit = 0x0100 };
CIDebugSymbolGroup *symbolGroup,
const QStringList &uninitializedVariables,
QString *errorMessage);
QString prefix() const { return m_prefix; } static CdbSymbolGroupContext *create(const QString &prefix,
CIDebugSymbolGroup *symbolGroup,
const QSharedPointer<CdbDumperHelper> &dumper,
const QStringList &uninitializedVariables,
QString *errorMessage);
bool assignValue(const QString &iname, const QString &value, bool editorToolTip(const QString &iname, QString *value, QString *errorMessage);
QString *newValue /* = 0 */, 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. // Initially populate the locals model for a new stackframe.
// Write a sequence of WatchData to it, recurse down if the // Write a sequence of WatchData to it, recurse down if the
// recursionPredicate agrees. The ignorePredicate can be used // recursionPredicate agrees. The ignorePredicate can be used
// to terminate processing after insertion of an item (if the calling // to terminate processing after insertion of an item (if the calling
// routine wants to insert another subtree). // routine wants to insert another subtree).
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate> template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
static bool populateModelInitially(const CdbSymbolGroupRecursionContext &ctx, static bool populateModelInitiallyHelper(const CdbSymbolGroupRecursionContext &ctx,
OutputIterator it, OutputIterator it,
RecursionPredicate recursionPredicate, RecursionPredicate recursionPredicate,
IgnorePredicate ignorePredicate, IgnorePredicate ignorePredicate,
@@ -102,7 +100,7 @@ public:
// to terminate processing after insertion of an item (if the calling // to terminate processing after insertion of an item (if the calling
// routine wants to insert another subtree). // routine wants to insert another subtree).
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate> template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
static bool completeData (const CdbSymbolGroupRecursionContext &ctx, static bool completeDataHelper (const CdbSymbolGroupRecursionContext &ctx,
WatchData incompleteLocal, WatchData incompleteLocal,
OutputIterator it, OutputIterator it,
RecursionPredicate recursionPredicate, RecursionPredicate recursionPredicate,
@@ -116,67 +114,30 @@ public:
// Retrieve child symbols of prefix as a sequence of WatchData. // Retrieve child symbols of prefix as a sequence of WatchData.
// Is CIDebugDataSpaces is != 0, try internal dumper and set owner // Is CIDebugDataSpaces is != 0, try internal dumper and set owner
template <class OutputIterator> template <class OutputIterator>
bool getDumpChildSymbols(CIDebugDataSpaces *ds, const QString &prefix, bool getDumpChildSymbols(const QString &prefix,
int dumpedOwner, int dumpedOwner,
OutputIterator it, QString *errorMessage); OutputIterator it, QString *errorMessage);
WatchData watchDataAt(unsigned long index) const; template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
// Run the internal dumpers on the symbol static bool insertSymbolRecursion(WatchData wd,
WatchData dumpSymbolAt(CIDebugDataSpaces *ds, unsigned long index); const CdbSymbolGroupRecursionContext &ctx,
OutputIterator it,
RecursionPredicate recursionPredicate,
IgnorePredicate ignorePredicate,
QString *errorMessage);
bool lookupPrefix(const QString &prefix, unsigned long *index) const; unsigned watchDataAt(unsigned long index, WatchData *);
enum SymbolState { LeafSymbol, ExpandedSymbol, CollapsedSymbol }; const bool m_useDumpers;
SymbolState symbolState(unsigned long index) const; const QSharedPointer<CdbDumperHelper> m_dumper;
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
enum DumperResult { DumperOk, DumperError, DumperNotHandled };
DumperResult dump(CIDebugDataSpaces *ds, WatchData *wd);
private:
typedef QMap<QString, unsigned long> NameIndexMap;
static inline bool isSymbolDisplayable(const DEBUG_SYMBOL_PARAMETERS &p);
bool init(QString *errorMessage);
void clear();
QString toString(bool verbose = false) const;
bool getChildSymbolsPosition(const QString &prefix,
unsigned long *startPos,
unsigned long *parentId,
QString *errorMessage);
bool expandSymbol(const QString &prefix, unsigned long index, QString *errorMessage);
void populateINameIndexMap(const QString &prefix, unsigned long parentId, unsigned long end);
QString symbolINameAt(unsigned long index) const;
int dumpQString(CIDebugDataSpaces *ds, WatchData *wd);
int dumpStdString(WatchData *wd);
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;
NameIndexMap m_inameIndexMap;
QVector<DEBUG_SYMBOL_PARAMETERS> m_symbolParameters;
int m_unnamedSymbolNumber;
}; };
// A convenience struct to save parameters for the model recursion. // A convenience struct to save parameters for the model recursion.
struct CdbSymbolGroupRecursionContext { struct CdbSymbolGroupRecursionContext {
explicit CdbSymbolGroupRecursionContext(CdbSymbolGroupContext *ctx, int internalDumperOwner, CIDebugDataSpaces *ds); explicit CdbSymbolGroupRecursionContext(CdbSymbolGroupContext *ctx, int internalDumperOwner);
CdbSymbolGroupContext *context; CdbSymbolGroupContext *context;
int internalDumperOwner; int internalDumperOwner;
CIDebugDataSpaces *dataspaces;
}; };
// Helper to a sequence of WatchData into a list. // Helper to a sequence of WatchData into a list.

View File

@@ -30,6 +30,8 @@
#ifndef CDBSYMBOLGROUPCONTEXT_TPL_H #ifndef CDBSYMBOLGROUPCONTEXT_TPL_H
#define CDBSYMBOLGROUPCONTEXT_TPL_H #define CDBSYMBOLGROUPCONTEXT_TPL_H
#include "watchhandler.h"
#include <QtCore/QDebug> #include <QtCore/QDebug>
namespace Debugger { namespace Debugger {
@@ -37,20 +39,10 @@ namespace Internal {
enum { debugSgRecursion = 0 }; enum { debugSgRecursion = 0 };
/* inline static */ bool CdbSymbolGroupContext::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;
}
template <class OutputIterator> template <class OutputIterator>
bool CdbSymbolGroupContext::getDumpChildSymbols(CIDebugDataSpaces *ds, const QString &prefix, bool CdbSymbolGroupContext::getDumpChildSymbols(const QString &prefix,
int dumpedOwner, int dumpedOwner,
OutputIterator it, QString *errorMessage) OutputIterator it, QString *errorMessage)
{ {
unsigned long start; unsigned long start;
unsigned long parentId; unsigned long parentId;
@@ -58,21 +50,14 @@ bool CdbSymbolGroupContext::getDumpChildSymbols(CIDebugDataSpaces *ds, const QSt
return false; return false;
// Skip over expanded children. Internal dumping might expand // Skip over expanded children. Internal dumping might expand
// children, so, re-evaluate size in end condition. // children, so, re-evaluate size in end condition.
for (int s = start; s < m_symbolParameters.size(); ++s) { const int count = size();
const DEBUG_SYMBOL_PARAMETERS &p = m_symbolParameters.at(s); for (int s = start; s < count; ++s) {
const DEBUG_SYMBOL_PARAMETERS &p = symbolParameterAt(s);
if (p.ParentSymbol == parentId && isSymbolDisplayable(p)) { if (p.ParentSymbol == parentId && isSymbolDisplayable(p)) {
WatchData wd = watchDataAt(s); WatchData wd;
// Run internal dumper, mark ownership const unsigned rc = watchDataAt(s, &wd);
if (ds) { if (rc & InternalDumperMask)
switch (dump(ds, &wd)) { wd.source = dumpedOwner;
case DumperOk:
case DumperError: // Not initialized yet, do not run other dumpers
wd.source = dumpedOwner;
break;
case DumperNotHandled:
break;
}
}
*it = wd; *it = wd;
++it; ++it;
} }
@@ -86,7 +71,7 @@ bool CdbSymbolGroupContext::getDumpChildSymbols(CIDebugDataSpaces *ds, const QSt
// children but can expand none, which would lead to invalid parent information // children but can expand none, which would lead to invalid parent information
// (expand icon), though (ignore for simplicity). // (expand icon), though (ignore for simplicity).
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate> template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
bool insertSymbolRecursion(WatchData wd, bool CdbSymbolGroupContext::insertSymbolRecursion(WatchData wd,
const CdbSymbolGroupRecursionContext &ctx, const CdbSymbolGroupRecursionContext &ctx,
OutputIterator it, OutputIterator it,
RecursionPredicate recursionPredicate, RecursionPredicate recursionPredicate,
@@ -124,8 +109,7 @@ bool insertSymbolRecursion(WatchData wd,
return true; return true;
QList<WatchData> watchList; QList<WatchData> watchList;
// This implicitly enforces expansion // This implicitly enforces expansion
if (!ctx.context->getDumpChildSymbols(ctx.dataspaces, if (!ctx.context->getDumpChildSymbols(wd.iname,
wd.iname,
ctx.internalDumperOwner, ctx.internalDumperOwner,
WatchDataBackInserter(watchList), errorMessage)) WatchDataBackInserter(watchList), errorMessage))
return false; return false;
@@ -145,11 +129,11 @@ bool insertSymbolRecursion(WatchData wd,
} }
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate> template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
bool CdbSymbolGroupContext::populateModelInitially(const CdbSymbolGroupRecursionContext &ctx, bool CdbSymbolGroupContext::populateModelInitiallyHelper(const CdbSymbolGroupRecursionContext &ctx,
OutputIterator it, OutputIterator it,
RecursionPredicate recursionPredicate, RecursionPredicate recursionPredicate,
IgnorePredicate ignorePredicate, IgnorePredicate ignorePredicate,
QString *errorMessage) QString *errorMessage)
{ {
if (debugSgRecursion) if (debugSgRecursion)
qDebug() << "### CdbSymbolGroupContext::populateModelInitially"; qDebug() << "### CdbSymbolGroupContext::populateModelInitially";
@@ -157,7 +141,7 @@ bool CdbSymbolGroupContext::populateModelInitially(const CdbSymbolGroupRecursion
// Insert root items // Insert root items
QList<WatchData> watchList; QList<WatchData> watchList;
CdbSymbolGroupContext *sg = ctx.context; CdbSymbolGroupContext *sg = ctx.context;
if (!sg->getDumpChildSymbols(ctx.dataspaces, sg->prefix(), if (!sg->getDumpChildSymbols(sg->prefix(),
ctx.internalDumperOwner, ctx.internalDumperOwner,
WatchDataBackInserter(watchList), errorMessage)) WatchDataBackInserter(watchList), errorMessage))
return false; return false;
@@ -169,7 +153,7 @@ bool CdbSymbolGroupContext::populateModelInitially(const CdbSymbolGroupRecursion
} }
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate> template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
bool CdbSymbolGroupContext::completeData(const CdbSymbolGroupRecursionContext &ctx, bool CdbSymbolGroupContext::completeDataHelper(const CdbSymbolGroupRecursionContext &ctx,
WatchData incompleteLocal, WatchData incompleteLocal,
OutputIterator it, OutputIterator it,
RecursionPredicate recursionPredicate, RecursionPredicate recursionPredicate,

View File

@@ -0,0 +1,412 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "stacktracecontext.h"
#include "symbolgroupcontext.h"
#include "breakpoint.h"
#include "coreengine.h"
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QTextStream>
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);
}
// 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)
{
}
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))
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;
}
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;
}
hr = m_cif->debugSymbols->SetScope(0, m_cdbFrames + index, NULL, 0);
if (FAILED(hr)) {
*errorMessage = CdbCore::msgComFailed("SetScope", hr);
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;
}
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::getThreadIds(const CdbCore::ComInterfaces &cif,
QVector<ULONG> *threadIds,
ULONG *currentThreadId,
QString *errorMessage)
{
threadIds->clear();
ULONG threadCount;
*currentThreadId = 0;
HRESULT hr= cif.debugSystemObjects->GetNumberThreads(&threadCount);
if (FAILED(hr)) {
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetNumberThreads", hr));
return false;
}
// Get ids and index of current
if (!threadCount)
return true;
hr = cif.debugSystemObjects->GetCurrentThreadId(currentThreadId);
if (FAILED(hr)) {
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetCurrentThreadId", hr));
return false;
}
threadIds->resize(threadCount);
hr = cif.debugSystemObjects->GetThreadIdsByIndex(0, threadCount, &(*threadIds->begin()), 0);
if (FAILED(hr)) {
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetThreadIdsByIndex", hr));
return false;
}
return true;
}
bool StackTraceContext::getThreads(const CdbCore::ComInterfaces &cif,
ThreadIdFrameMap *threads,
ULONG *currentThreadId,
QString *errorMessage)
{
threads->clear();
QVector<ULONG> threadIds;
if (!getThreadIds(cif, &threadIds, currentThreadId, errorMessage))
return false;
if (threadIds.isEmpty())
return true;
const int threadCount = threadIds.size();
for (int i = 0; i < threadCount; i++) {
const ULONG id = threadIds.at(i);
StackFrame frame;
if (!getStoppedThreadState(cif, id, &frame, errorMessage)) {
qWarning("%s\n", qPrintable(*errorMessage));
errorMessage->clear();
}
threads->insert(id, frame);
}
// Restore thread id
if (threadIds.back() != *currentThreadId) {
const HRESULT hr = cif.debugSystemObjects->SetCurrentThreadId(*currentThreadId);
if (FAILED(hr)) {
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("SetCurrentThreadId", hr));
return false;
}
}
return true;
}
QString StackTraceContext::formatThreads(const ThreadIdFrameMap &threads)
{
QString rc;
QTextStream str(&rc);
const ThreadIdFrameMap::const_iterator cend = threads.constEnd();
for (ThreadIdFrameMap::const_iterator it = threads.constBegin(); it != cend; ++it) {
str << '#' << it.key() << ' ';
it.value().format(str);
str << '\n';
}
return rc;
}
QDebug operator<<(QDebug d, const StackTraceContext &t)
{
d.nospace() << t.toString();
return d;
}
}

View File

@@ -0,0 +1,162 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#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;
};
QDebug operator<<(QDebug d, const StackFrame &);
/* 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);
// A map of thread id, stack frame
typedef QMap<unsigned long, StackFrame> ThreadIdFrameMap;
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 inline bool getThreadIds(const CdbCore::ComInterfaces &cif,
QVector<ULONG> *threadIds,
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);
// Retrieve detailed information about all threads, works in stopped state.
static bool getThreads(const CdbCore::ComInterfaces &cif,
ThreadIdFrameMap *threads,
ULONG *currentThreadId,
QString *errorMessage);
static QString formatThreads(const ThreadIdFrameMap &threads);
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:
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;
};
QDebug operator<<(QDebug d, const StackTraceContext &);
} // namespace Internal
#endif // CORESTACKTRACECONTEXT_H

View File

@@ -0,0 +1,740 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "symbolgroupcontext.h"
#include "coreengine.h"
#include <QtCore/QTextStream>
#include <QtCore/QCoreApplication>
#include <QtCore/QRegExp>
#include <QtCore/QString>
#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[nameLength] = 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('>');
}
// 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);
}
// 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;
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)
{
*value = 0;
if (!stringValue.startsWith(QLatin1String("0x")))
return false;
stringValue.remove(0, 2);
// Remove 64bit separator
if (stringValue.size() > 9) {
const int sepPos = stringValue.size() - 9;
if (stringValue.at(sepPos) == QLatin1Char('`'))
stringValue.remove(sepPos, 1);
}
bool ok;
*value = stringValue.toULongLong(&ok, 16);
return ok;
}
// 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 & (HasChildren|OutOfScope)) )
break;
// QString
if (typeNameIn->endsWith(QLatin1String("QString")) || typeNameIn->endsWith(QLatin1String("QString *"))) {
const int drc = dumpQString(index, *inameIn, valueIn);
switch (drc) {
case 0:
rc |= InternalDumperSucceeded;
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;
break;
case 1:
rc |= InternalDumperError;
break;
default:
rc |= InternalDumperFailed;
qWarning("%s\n", qPrintable(msgStructuralError(*inameIn, *typeNameIn, drc)));
break;
}
}
} while (false);
if (debugInternalDumpers)
qDebug() << "SymbolGroupContext::dump" << rc << nameIn << valueIn;
return rc;
}
// Get integer value of symbol group
bool SymbolGroupContext::getDecimalIntValue(CIDebugSymbolGroup *sg, int index, int *value)
{
const QString valueS = getSymbolString(sg, &IDebugSymbolGroup2::GetSymbolValueTextWide, index);
bool ok;
*value = valueS.toInt(&ok);
return ok;
}
// 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(iname, dIndex, &errorMessage))
return 3;
const unsigned long sizeIndex = dIndex + 3;
const unsigned long arrayIndex = dIndex + 4;
// Get size and pointer
int size;
if (!getDecimalIntValue(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 * 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;
const unsigned long bxIndex = index + 3;
if (!expandSymbol(inameIn, bxIndex, &errorMessage))
return 2;
// Check if size is something sane
const int sizeIndex = index + 6;
int size;
if (!getDecimalIntValue(m_symbolGroup, sizeIndex, &size))
return 3;
if (size < 0)
return 1;
// Just copy over the value of the buf[]-array, which should be the string
const QChar doubleQuote = QLatin1Char('"');
const int bufIndex = index + 4;
*valueIn = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, bufIndex);
const int quotePos = valueIn->indexOf(doubleQuote);
if (quotePos == -1)
return 1;
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

View File

@@ -0,0 +1,183 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#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 32bit values (returned as dec)
static bool getDecimalIntValue(CIDebugSymbolGroup *sg, int index, int *value);
// For pointers and 64bit values (returned as hex)
static bool getUnsignedHexValue(QString stringValue, quint64 *value);
// 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);
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

View File

@@ -324,14 +324,19 @@ QString WatchData::msgNotInScope()
return rc; return rc;
} }
const QString &WatchData::shadowedNameFormat()
{
static const QString format = QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>");
return format;
}
QString WatchData::shadowedName(const QString &name, int seen) QString WatchData::shadowedName(const QString &name, int seen)
{ {
if (seen <= 0) if (seen <= 0)
return name; return name;
return QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>").arg(name).arg(seen); return shadowedNameFormat().arg(name, seen);
} }
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// //
// WatchModel // WatchModel

View File

@@ -117,6 +117,7 @@ public:
static QString msgNotInScope(); static QString msgNotInScope();
static QString shadowedName(const QString &name, int seen); static QString shadowedName(const QString &name, int seen);
static const QString &shadowedNameFormat();
public: public:
QByteArray iname; // internal name sth like 'local.baz.public.a' QByteArray iname; // internal name sth like 'local.baz.public.a'

View File

@@ -32,6 +32,8 @@
#include "cdbdebugoutput.h" #include "cdbdebugoutput.h"
#include "cdbpromptthread.h" #include "cdbpromptthread.h"
#include "debugeventcallback.h" #include "debugeventcallback.h"
#include "symbolgroupcontext.h"
#include "stacktracecontext.h"
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QtCore/QTimer> #include <QtCore/QTimer>
@@ -149,6 +151,45 @@ void CdbApplication::asyncCommand(int command, const QString &arg)
} }
} }
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));
}
bool CdbApplication::queueBreakPoint(const QString &arg)
{
// Queue file:line
const int cpos = arg.lastIndexOf(QLatin1Char(':'));
if (cpos == -1)
return false;
CdbCore::BreakPoint bp;
bp.fileName = arg.left(cpos);
bool ok;
bp.lineNumber = arg.mid(cpos + 1).toInt(&ok);
if (!ok || bp.lineNumber < 1)
return false;
m_queuedBreakPoints.push_back(bp);
return true;
}
void CdbApplication::syncCommand(int command, const QString &arg) void CdbApplication::syncCommand(int command, const QString &arg)
{ {
QString errorMessage; QString errorMessage;
@@ -163,6 +204,34 @@ void CdbApplication::syncCommand(int command, const QString &arg)
} }
} }
break; 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 {
if (queueBreakPoint(targs)) {
std::printf("Queueing breakpoint %s\n", qPrintable(targs));
} else {
std::printf("BREAKPOINT SYNTAX ERROR: %s\n", qPrintable(targs));
}
}
}
break;
case Sync_PrintFrame:
printFrame(arg);
break;
case Unknown: case Unknown:
if (!m_engine->executeDebuggerCommand(arg, &errorMessage)) if (!m_engine->executeDebuggerCommand(arg, &errorMessage))
std::printf("%s\n", qPrintable(errorMessage)); std::printf("%s\n", qPrintable(errorMessage));
@@ -196,6 +265,7 @@ void CdbApplication::executionCommand(int command, const QString &arg)
} }
if (ok) { if (ok) {
m_engine->startWatchTimer(); m_engine->startWatchTimer();
m_stackTrace.reset();
} else { } else {
std::fprintf(stderr, "%s\n", qPrintable(errorMessage)); std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
} }
@@ -203,11 +273,50 @@ void CdbApplication::executionCommand(int command, const QString &arg)
void CdbApplication::debugEvent() void CdbApplication::debugEvent()
{ {
QString errorMessage;
std::printf("Debug event\n"); std::printf("Debug event\n");
CdbCore::StackTraceContext::ThreadIdFrameMap threads;
ULONG currentThreadId;
if (CdbCore::StackTraceContext::getThreads(m_engine->interfaces(), &threads,
&currentThreadId, &errorMessage)) {
printf("%s\n", qPrintable(CdbCore::StackTraceContext::formatThreads(threads)));
} else {
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
}
CdbCore::StackTraceContext *trace =
CdbCore::StackTraceContext::create(&m_engine->interfaces(),
0xFFFF, &errorMessage);
if (trace) {
m_stackTrace.reset(trace);
printf("%s\n", qPrintable(m_stackTrace->toString()));
} else {
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
}
} }
void CdbApplication::processAttached(void *handle) void CdbApplication::processAttached(void *handle)
{ {
std::printf("pe\n"); std::printf("### Process attached\n");
m_processHandle = handle; 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 CdbCore::BreakPoint &bp, m_queuedBreakPoints) {
if (bp.add(m_engine->interfaces().debugControl, &errorMessage)) {
std::printf("'%s' [ok]\n", qPrintable(bp.expression()));
} else {
std::fprintf(stderr, "%s: %s\n",
qPrintable(bp.expression()),
qPrintable(errorMessage));
}
}
} }

View File

@@ -30,11 +30,16 @@
#ifndef CDBAPPLICATION_H #ifndef CDBAPPLICATION_H
#define CDBAPPLICATION_H #define CDBAPPLICATION_H
#include "breakpoint.h"
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QSharedPointer> #include <QtCore/QSharedPointer>
#include <QtCore/QScopedPointer>
#include <QtCore/QStringList>
namespace CdbCore { namespace CdbCore {
class CoreEngine; class CoreEngine;
class StackTraceContext;
} }
class CdbPromptThread; class CdbPromptThread;
@@ -61,10 +66,16 @@ private slots:
private: private:
bool parseOptions(); bool parseOptions();
void printFrame(const QString &arg);
bool queueBreakPoint(const QString &arg);
QString m_engineDll; QString m_engineDll;
QSharedPointer<CdbCore::CoreEngine> m_engine; QSharedPointer<CdbCore::CoreEngine> m_engine;
QScopedPointer<CdbCore::StackTraceContext> m_stackTrace;
CdbPromptThread *m_promptThread; CdbPromptThread *m_promptThread;
QStringList m_queuedCommands;
QList<CdbCore::BreakPoint> m_queuedBreakPoints;
void *m_processHandle; void *m_processHandle;
}; };

View File

@@ -40,6 +40,11 @@ static const char help[] =
"S binary args Start binary\n" "S binary args Start binary\n"
"I Interrupt\n" "I Interrupt\n"
"G Go\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"
"F <n> Print stack frame <n>, 0 being top\n"
"\nThe remaining commands are passed to CDB.\n"; "\nThe remaining commands are passed to CDB.\n";
CdbPromptThread::CdbPromptThread(QObject *parent) : CdbPromptThread::CdbPromptThread(QObject *parent) :
@@ -75,12 +80,18 @@ static Command evaluateCommand(const QString &cmdToken)
switch(cmdToken.at(0).toAscii()) { switch(cmdToken.at(0).toAscii()) {
case 'I': case 'I':
return Async_Interrupt; return Async_Interrupt;
case 'Q':
return Sync_Queue;
case 'B':
return Sync_QueueBreakPoint;
case 'E': case 'E':
return Sync_EvalExpression; return Sync_EvalExpression;
case 'G': case 'G':
return Execution_Go; return Execution_Go;
case 'S': case 'S':
return Execution_StartBinary; return Execution_StartBinary;
case 'F':
return Sync_PrintFrame;
default: default:
break; break;
} }

View File

@@ -47,6 +47,9 @@ enum Command {
UnknownCommand = 0, UnknownCommand = 0,
Async_Interrupt = AsyncCommand|1, Async_Interrupt = AsyncCommand|1,
Sync_EvalExpression = SyncCommand|1, Sync_EvalExpression = SyncCommand|1,
Sync_Queue = SyncCommand|2,
Sync_QueueBreakPoint = SyncCommand|3,
Sync_PrintFrame = SyncCommand|4,
Execution_Go = ExecutionCommand|1, Execution_Go = ExecutionCommand|1,
Execution_StartBinary = ExecutionCommand|2 Execution_StartBinary = ExecutionCommand|2
}; };