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_tpl.h \
$$PWD/cdbstacktracecontext.h \
$$PWD/cdbstackframecontext.h \
$$PWD/cdbbreakpoint.h \
$$PWD/cdbmodules.h \
$$PWD/cdbassembler.h \
@@ -25,7 +24,6 @@ SOURCES += \
$$PWD/cdbdebugeventcallback.cpp \
$$PWD/cdbdebugoutput.cpp \
$$PWD/cdbsymbolgroupcontext.cpp \
$$PWD/cdbstackframecontext.cpp \
$$PWD/cdbstacktracecontext.cpp \
$$PWD/cdbbreakpoint.cpp \
$$PWD/cdbmodules.cpp \
@@ -37,6 +35,4 @@ SOURCES += \
$$PWD/cdbexceptionutils.cpp
FORMS += $$PWD/cdboptionspagewidget.ui
LIBS+=-lpsapi
}

View File

@@ -29,482 +29,13 @@
#include "cdbbreakpoint.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 Internal {
// The CDB breakpoint expression syntax is:
// `foo.cpp:523`[ "condition"]
// module!function[ "condition"]
enum { debugBP = 0 };
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)
{
@@ -512,17 +43,18 @@ static inline QString msgCannotSetBreakAtFunction(const QString &func, const QSt
}
// Synchronize (halted) engine breakpoints with those of the BreakHandler.
bool CDBBreakPoint::synchronizeBreakPoints(CIDebugControl* debugControl,
CIDebugSymbols *syms,
BreakHandler *handler,
QString *errorMessage, QStringList *warnings)
bool synchronizeBreakPoints(CIDebugControl* debugControl,
CIDebugSymbols *syms,
BreakHandler *handler,
QString *errorMessage,
QStringList *warnings)
{
errorMessage->clear();
warnings->clear();
// Do an initial check whether we are in a state that allows
// for modifying breakPoints
ULONG engineCount;
if (!getBreakPointCount(debugControl, &engineCount, errorMessage)) {
if (!CdbCore::BreakPoint::getBreakPointCount(debugControl, &engineCount, errorMessage)) {
*errorMessage = QString::fromLatin1("Cannot modify breakpoints: %1").arg(*errorMessage);
return false;
}
@@ -556,7 +88,7 @@ bool CDBBreakPoint::synchronizeBreakPoints(CIDebugControl* debugControl,
if (breakPointOk) {
quint64 address;
unsigned long id;
CDBBreakPoint ncdbbp(*nbd);
const CdbCore::BreakPoint ncdbbp = breakPointFromBreakPointData(*nbd);
breakPointOk = ncdbbp.add(debugControl, &warning, &id, &address);
if (breakPointOk) {
if (debugBP)
@@ -578,24 +110,24 @@ bool CDBBreakPoint::synchronizeBreakPoints(CIDebugControl* debugControl,
warnings->push_back(warning); }
// Delete
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);
delete rbd;
}
// Enable/Disable
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);
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);
if (updateMarkers)
handler->updateMarkers();
if (debugBP > 1) {
QList<CDBBreakPoint> bps;
CDBBreakPoint::getBreakPoints(debugControl, &bps, errorMessage);
QList<CdbCore::BreakPoint> bps;
CdbCore::BreakPoint::getBreakPoints(debugControl, &bps, errorMessage);
qDebug().nospace() << "### Breakpoints in engine: " << bps;
}
return true;

View File

@@ -31,73 +31,39 @@
#define CDBBREAKPOINTS_H
#include "cdbcom.h"
#include "breakpoint.h"
#include "breakhandler.h"
#include <QtCore/QString>
#include <QtCore/QList>
#include <QtCore/QDir>
QT_BEGIN_NAMESPACE
class QDebug;
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 Internal {
class BreakHandler;
class BreakpointData;
// Synchronize (halted) engine with BreakHandler.
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 Debugger

View File

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

View File

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

View File

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

View File

@@ -75,7 +75,7 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
QString 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);
if (debugCDB)

View File

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

View File

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

View File

@@ -310,15 +310,14 @@ bool CdbStackFrameContext::populateModelInitially(WatchHandler *wh, QString *err
qDebug() << "populateModelInitially dumpers=" << m_useDumpers;
// Recurse down items that are initially expanded in the view, stop processing for
// dumper items.
const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper,
m_dumper->comInterfaces()->debugDataSpaces);
const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper);
const bool rc = m_useDumpers ?
CdbSymbolGroupContext::populateModelInitially(rctx,
CdbSymbolGroupContext::populateModelInitiallyHelper(rctx,
WatchHandleDumperInserter(wh, m_dumper),
WatchHandlerExpandedPredicate(wh),
isDumperPredicate,
errorMessage) :
CdbSymbolGroupContext::populateModelInitially(rctx,
CdbSymbolGroupContext::populateModelInitiallyHelper(rctx,
WatchHandlerModelInserter(wh),
WatchHandlerExpandedPredicate(wh),
falsePredicate,
@@ -333,11 +332,10 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal,
if (debugCDBWatchHandling)
qDebug() << ">completeData src=" << incompleteLocal.source << incompleteLocal.toString();
const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper,
m_dumper->comInterfaces()->debugDataSpaces);
const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper);
// Expand symbol group items, recurse one level from desired item
if (!m_useDumpers) {
return CdbSymbolGroupContext::completeData(rctx, incompleteLocal,
return CdbSymbolGroupContext::completeDataHelper(rctx, incompleteLocal,
WatchHandlerModelInserter(wh),
MatchINamePredicate(incompleteLocal.iname),
falsePredicate,
@@ -375,7 +373,7 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal,
}
// Expand symbol group items, recurse one level from desired item
return CdbSymbolGroupContext::completeData(rctx, incompleteLocal,
return CdbSymbolGroupContext::completeDataHelper(rctx, incompleteLocal,
WatchHandleDumperInserter(wh, m_dumper),
MatchINamePredicate(incompleteLocal.iname),
isDumperPredicate,
@@ -398,8 +396,12 @@ bool CdbStackFrameContext::editorToolTip(const QString &iname,
return false;
}
// 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;
if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, &result, errorMessage)) {
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 "coreengine.h"
#include "cdbstackframecontext.h"
#include "cdbbreakpoint.h"
#include "cdbsymbolgroupcontext.h"
#include "cdbdebugengine_p.h"
#include "cdbdumperhelper.h"
#include "cdbdebugengine_p.h"
#include "debuggeractions.h"
#include "debuggermanager.h"
#include "watchutils.h"
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QTextStream>
enum { debug = 1 };
namespace Debugger {
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) :
m_dumper(dumper),
m_cif(dumper->comInterfaces()),
m_instructionOffset(0)
CdbCore::StackTraceContext(dumper->comInterfaces()),
m_dumper(dumper)
{
}
CdbStackTraceContext *CdbStackTraceContext::create(const QSharedPointer<CdbDumperHelper> &dumper,
unsigned long threadId,
QString *errorMessage)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << threadId;
// fill the DEBUG_STACK_FRAME array
ULONG frameCount;
CdbStackTraceContext *ctx = new CdbStackTraceContext(dumper);
const HRESULT hr = dumper->comInterfaces()->debugControl->GetStackTrace(0, 0, 0, ctx->m_cdbFrames, CdbStackTraceContext::maxFrames, &frameCount);
if (FAILED(hr)) {
delete ctx;
*errorMessage = CdbCore::msgComFailed("GetStackTrace", hr);
return 0;
}
if (!ctx->init(frameCount, errorMessage)) {
if (!ctx->init(UINT_MAX, errorMessage)) {
delete ctx;
return 0;
}
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
QStringList uninitializedVariables;
if (theDebuggerAction(UseCodeModel)->isChecked()) {
const StackFrame &frame = m_frames.at(index);
getUninitializedVariables(DebuggerManager::instance()->cppCodeModelSnapshot(), frame.function, frame.file, frame.line, &uninitializedVariables);
}
CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(QLatin1String("local"), sg, uninitializedVariables, errorMessage);
const CdbCore::StackFrame &frame = stackFrameAt(index);
if (theDebuggerAction(UseCodeModel)->isChecked())
getUninitializedVariables(DebuggerManager::instance()->cppCodeModelSnapshot(), frame.function, frame.fileName, frame.line, &uninitializedVariables);
if (debug)
qDebug() << frame << uninitializedVariables;
CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(prefix,
comSymbolGroup,
m_dumper,
uninitializedVariables,
errorMessage);
if (!sc) {
*errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage);
*errorMessage = msgFrameContextFailed(index, frame, *errorMessage);
return 0;
}
CdbStackFrameContext *fr = new CdbStackFrameContext(m_dumper, sc);
m_frameContexts[index] = fr;
return fr;
return sc;
}
CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *errorMessage)
CdbSymbolGroupContext *CdbStackTraceContext::cdbSymbolGroupContextAt(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;
return static_cast<CdbSymbolGroupContext *>(symbolGroupContextAt(index, errorMessage));
}
QString CdbStackTraceContext::toString() const
QList<StackFrame> CdbStackTraceContext::stackFrames() const
{
QString rc;
QTextStream str(&rc);
format(str);
// Convert from Core data structures
QList<StackFrame> rc;
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;
}
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 isStopped,
QList<ThreadData> *threads,
ULONG *currentThreadId,
QString *errorMessage)
{
// Convert from Core data structures
threads->clear();
ULONG threadCount;
*currentThreadId = 0;
HRESULT hr= cif.debugSystemObjects->GetNumberThreads(&threadCount);
if (FAILED(hr)) {
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetNumberThreads", hr));
ThreadIdFrameMap threadMap;
if (!CdbCore::StackTraceContext::getThreads(cif, &threadMap,
currentThreadId, errorMessage))
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;
}
QVector<ULONG> threadIds(threadCount);
hr = cif.debugSystemObjects->GetThreadIdsByIndex(0, threadCount, &(*threadIds.begin()), 0);
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;
}
const QChar slash = QLatin1Char('/');
const ThreadIdFrameMap::const_iterator cend = threadMap.constEnd();
for (ThreadIdFrameMap::const_iterator it = threadMap.constBegin(); it != cend; ++it) {
ThreadData data(it.key());
const CdbCore::StackFrame &coreFrame = it.value();
data.address = coreFrame.address;
data.function = coreFrame.function;
data.line = coreFrame.line;
// Basename only for brevity
const int slashPos = coreFrame.fileName.lastIndexOf(slash);
data.file = slashPos == -1 ? coreFrame.fileName : coreFrame.fileName.mid(slashPos + 1);
threads->push_back(data);
}
return true;
}

View File

@@ -31,6 +31,7 @@
#define CDBSTACKTRACECONTEXT_H
#include "stackhandler.h"
#include "stacktracecontext.h"
#include "cdbcom.h"
@@ -54,65 +55,38 @@ class CdbStackFrameContext;
class CdbDumperHelper;
struct ThreadData;
/* Context representing a break point stack consisting of several frames.
* Maintains an on-demand constructed list of CdbStackFrameContext
* containining the local variables of the stack. */
/* CdbStackTraceContext: Bridges CdbCore data structures and
* Debugger structures and handles CdbSymbolGroupContext's. */
class CdbStackTraceContext
class CdbStackTraceContext : public CdbCore::StackTraceContext
{
Q_DISABLE_COPY(CdbStackTraceContext)
explicit CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper);
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,
unsigned long threadid,
QString *errorMessage);
QList<StackFrame> frames() const { return m_frames; }
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;
CdbSymbolGroupContext *cdbSymbolGroupContextAt(int index, QString *errorMessage);
// Top-Level instruction offset for disassembler
ULONG64 instructionOffset() const { return m_instructionOffset; }
QList<StackFrame> stackFrames() const;
CdbStackFrameContext *frameContextAt(int index, QString *errorMessage);
// Format for logging
void format(QTextStream &str) const;
QString toString() const;
// Retrieve information about threads. When stopped, add
// current stack frame.
// get threads in stopped state
static bool getThreads(const CdbCore::ComInterfaces &cif,
bool isStopped,
QList<ThreadData> *threads,
ULONG *currentThreadId,
QString *errorMessage);
protected:
virtual CdbCore::SymbolGroupContext *createSymbolGroup(const CdbCore::ComInterfaces &cif,
int index,
const QString &prefix,
CIDebugSymbolGroup *comSymbolGroup,
QString *errorMessage);
private:
bool init(unsigned long frameCount, QString *errorMessage);
CIDebugSymbolGroup *createSymbolGroup(int index, QString *errorMessage);
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

File diff suppressed because it is too large Load Diff

View File

@@ -31,12 +31,13 @@
#define CDBSYMBOLGROUPCONTEXT_H
#include "cdbcom.h"
#include "watchhandler.h"
#include "symbolgroupcontext.h"
#include <QtCore/QString>
#include <QtCore/QVector>
#include <QtCore/QList>
#include <QtCore/QStringList>
#include <QtCore/QSharedPointer>
#include <QtCore/QPair>
#include <QtCore/QMap>
#include <QtCore/QSet>
@@ -47,50 +48,47 @@ namespace Internal {
class WatchData;
class WatchHandler;
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);
explicit CdbSymbolGroupContext(const QString &prefix,
CIDebugSymbolGroup *symbolGroup,
const QStringList &uninitializedVariables = QStringList());
const QSharedPointer<CdbDumperHelper> &dumper,
const QStringList &uninitializedVariables);
public:
~CdbSymbolGroupContext();
static CdbSymbolGroupContext *create(const QString &prefix,
CIDebugSymbolGroup *symbolGroup,
const QStringList &uninitializedVariables,
QString *errorMessage);
// Mask bits for the source field of watch data.
enum { SourceMask = 0xFF, ChildrenKnownBit = 0x0100 };
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,
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:
// Initially populate the locals model for a new stackframe.
// Write a sequence of WatchData to it, recurse down if the
// recursionPredicate agrees. The ignorePredicate can be used
// to terminate processing after insertion of an item (if the calling
// routine wants to insert another subtree).
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
static bool populateModelInitially(const CdbSymbolGroupRecursionContext &ctx,
static bool populateModelInitiallyHelper(const CdbSymbolGroupRecursionContext &ctx,
OutputIterator it,
RecursionPredicate recursionPredicate,
IgnorePredicate ignorePredicate,
@@ -102,7 +100,7 @@ public:
// to terminate processing after insertion of an item (if the calling
// routine wants to insert another subtree).
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
static bool completeData (const CdbSymbolGroupRecursionContext &ctx,
static bool completeDataHelper (const CdbSymbolGroupRecursionContext &ctx,
WatchData incompleteLocal,
OutputIterator it,
RecursionPredicate recursionPredicate,
@@ -116,67 +114,30 @@ public:
// Retrieve child symbols of prefix as a sequence of WatchData.
// Is CIDebugDataSpaces is != 0, try internal dumper and set owner
template <class OutputIterator>
bool getDumpChildSymbols(CIDebugDataSpaces *ds, const QString &prefix,
int dumpedOwner,
OutputIterator it, QString *errorMessage);
bool getDumpChildSymbols(const QString &prefix,
int dumpedOwner,
OutputIterator it, QString *errorMessage);
WatchData watchDataAt(unsigned long index) const;
// Run the internal dumpers on the symbol
WatchData dumpSymbolAt(CIDebugDataSpaces *ds, unsigned long index);
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
static bool insertSymbolRecursion(WatchData wd,
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 };
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
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;
const bool m_useDumpers;
const QSharedPointer<CdbDumperHelper> m_dumper;
};
// A convenience struct to save parameters for the model recursion.
struct CdbSymbolGroupRecursionContext {
explicit CdbSymbolGroupRecursionContext(CdbSymbolGroupContext *ctx, int internalDumperOwner, CIDebugDataSpaces *ds);
explicit CdbSymbolGroupRecursionContext(CdbSymbolGroupContext *ctx, int internalDumperOwner);
CdbSymbolGroupContext *context;
int internalDumperOwner;
CIDebugDataSpaces *dataspaces;
};
// Helper to a sequence of WatchData into a list.

View File

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

View File

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

View File

@@ -32,6 +32,8 @@
#include "cdbdebugoutput.h"
#include "cdbpromptthread.h"
#include "debugeventcallback.h"
#include "symbolgroupcontext.h"
#include "stacktracecontext.h"
#include <QtCore/QStringList>
#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)
{
QString errorMessage;
@@ -163,6 +204,34 @@ void CdbApplication::syncCommand(int command, const QString &arg)
}
}
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:
if (!m_engine->executeDebuggerCommand(arg, &errorMessage))
std::printf("%s\n", qPrintable(errorMessage));
@@ -196,6 +265,7 @@ void CdbApplication::executionCommand(int command, const QString &arg)
}
if (ok) {
m_engine->startWatchTimer();
m_stackTrace.reset();
} else {
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
}
@@ -203,11 +273,50 @@ void CdbApplication::executionCommand(int command, const QString &arg)
void CdbApplication::debugEvent()
{
QString errorMessage;
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)
{
std::printf("pe\n");
std::printf("### Process attached\n");
m_processHandle = handle;
QString errorMessage;
// Commands
foreach(const QString &qc, m_queuedCommands) {
if (m_engine->executeDebuggerCommand(qc, &errorMessage)) {
std::printf("'%s' [ok]\n", qPrintable(qc));
} else {
std::printf("%s\n", qPrintable(errorMessage));
}
}
// Breakpoints
foreach(const 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
#define CDBAPPLICATION_H
#include "breakpoint.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QSharedPointer>
#include <QtCore/QScopedPointer>
#include <QtCore/QStringList>
namespace CdbCore {
class CoreEngine;
class StackTraceContext;
}
class CdbPromptThread;
@@ -61,10 +66,16 @@ private slots:
private:
bool parseOptions();
void printFrame(const QString &arg);
bool queueBreakPoint(const QString &arg);
QString m_engineDll;
QSharedPointer<CdbCore::CoreEngine> m_engine;
QScopedPointer<CdbCore::StackTraceContext> m_stackTrace;
CdbPromptThread *m_promptThread;
QStringList m_queuedCommands;
QList<CdbCore::BreakPoint> m_queuedBreakPoints;
void *m_processHandle;
};

View File

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

View File

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