forked from qt-creator/qt-creator
CDB: Extract SymbolGroupContext class into Core library.
Split for testing/scripting purposes.
This commit is contained in:
493
src/plugins/debugger/cdb/breakpoint.cpp
Normal file
493
src/plugins/debugger/cdb/breakpoint.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
99
src/plugins/debugger/cdb/breakpoint.h
Normal file
99
src/plugins/debugger/cdb/breakpoint.h
Normal 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
|
||||||
@@ -10,7 +10,6 @@ HEADERS += \
|
|||||||
$$PWD/cdbsymbolgroupcontext.h \
|
$$PWD/cdbsymbolgroupcontext.h \
|
||||||
$$PWD/cdbsymbolgroupcontext_tpl.h \
|
$$PWD/cdbsymbolgroupcontext_tpl.h \
|
||||||
$$PWD/cdbstacktracecontext.h \
|
$$PWD/cdbstacktracecontext.h \
|
||||||
$$PWD/cdbstackframecontext.h \
|
|
||||||
$$PWD/cdbbreakpoint.h \
|
$$PWD/cdbbreakpoint.h \
|
||||||
$$PWD/cdbmodules.h \
|
$$PWD/cdbmodules.h \
|
||||||
$$PWD/cdbassembler.h \
|
$$PWD/cdbassembler.h \
|
||||||
@@ -25,7 +24,6 @@ SOURCES += \
|
|||||||
$$PWD/cdbdebugeventcallback.cpp \
|
$$PWD/cdbdebugeventcallback.cpp \
|
||||||
$$PWD/cdbdebugoutput.cpp \
|
$$PWD/cdbdebugoutput.cpp \
|
||||||
$$PWD/cdbsymbolgroupcontext.cpp \
|
$$PWD/cdbsymbolgroupcontext.cpp \
|
||||||
$$PWD/cdbstackframecontext.cpp \
|
|
||||||
$$PWD/cdbstacktracecontext.cpp \
|
$$PWD/cdbstacktracecontext.cpp \
|
||||||
$$PWD/cdbbreakpoint.cpp \
|
$$PWD/cdbbreakpoint.cpp \
|
||||||
$$PWD/cdbmodules.cpp \
|
$$PWD/cdbmodules.cpp \
|
||||||
@@ -37,6 +35,4 @@ SOURCES += \
|
|||||||
$$PWD/cdbexceptionutils.cpp
|
$$PWD/cdbexceptionutils.cpp
|
||||||
|
|
||||||
FORMS += $$PWD/cdboptionspagewidget.ui
|
FORMS += $$PWD/cdboptionspagewidget.ui
|
||||||
|
|
||||||
LIBS+=-lpsapi
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,482 +29,13 @@
|
|||||||
|
|
||||||
#include "cdbbreakpoint.h"
|
#include "cdbbreakpoint.h"
|
||||||
#include "cdbmodules.h"
|
#include "cdbmodules.h"
|
||||||
#include "breakhandler.h"
|
|
||||||
#include "cdbdebugengine_p.h"
|
|
||||||
|
|
||||||
#include <QtCore/QTextStream>
|
|
||||||
#include <QtCore/QDir>
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
#include <QtCore/QMap>
|
|
||||||
|
|
||||||
#include <psapi.h>
|
|
||||||
|
|
||||||
enum { debugBP = 0 };
|
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
// The CDB breakpoint expression syntax is:
|
enum { debugBP = 0 };
|
||||||
// `foo.cpp:523`[ "condition"]
|
|
||||||
// module!function[ "condition"]
|
|
||||||
|
|
||||||
static const char sourceFileQuoteC = '`';
|
|
||||||
|
|
||||||
CDBBreakPoint::CDBBreakPoint() :
|
|
||||||
ignoreCount(0),
|
|
||||||
lineNumber(-1),
|
|
||||||
oneShot(false),
|
|
||||||
enabled(true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CDBBreakPoint::CDBBreakPoint(const BreakpointData &bpd) :
|
|
||||||
fileName(QDir::toNativeSeparators(bpd.fileName)),
|
|
||||||
condition(bpd.condition),
|
|
||||||
ignoreCount(0),
|
|
||||||
funcName(bpd.funcName),
|
|
||||||
lineNumber(-1),
|
|
||||||
oneShot(false),
|
|
||||||
enabled(bpd.enabled)
|
|
||||||
{
|
|
||||||
if (!bpd.ignoreCount.isEmpty())
|
|
||||||
ignoreCount = bpd.ignoreCount.toInt();
|
|
||||||
if (!bpd.lineNumber.isEmpty())
|
|
||||||
lineNumber = bpd.lineNumber.toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
int CDBBreakPoint::compare(const CDBBreakPoint& rhs) const
|
|
||||||
{
|
|
||||||
if (ignoreCount > rhs.ignoreCount)
|
|
||||||
return 1;
|
|
||||||
if (ignoreCount < rhs.ignoreCount)
|
|
||||||
return -1;
|
|
||||||
if (lineNumber > rhs.lineNumber)
|
|
||||||
return 1;
|
|
||||||
if (lineNumber < rhs.lineNumber)
|
|
||||||
return -1;
|
|
||||||
if (oneShot && !rhs.oneShot)
|
|
||||||
return 1;
|
|
||||||
if (!oneShot && rhs.oneShot)
|
|
||||||
return -1;
|
|
||||||
if (enabled && !rhs.enabled)
|
|
||||||
return 1;
|
|
||||||
if (!enabled && rhs.enabled)
|
|
||||||
return -1;
|
|
||||||
if (const int fileCmp = fileName.compare(rhs.fileName))
|
|
||||||
return fileCmp;
|
|
||||||
if (const int funcCmp = funcName.compare(rhs.funcName))
|
|
||||||
return funcCmp;
|
|
||||||
if (const int condCmp = condition.compare(rhs.condition))
|
|
||||||
return condCmp;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDBBreakPoint::clear()
|
|
||||||
{
|
|
||||||
ignoreCount = 0;
|
|
||||||
oneShot = false;
|
|
||||||
enabled = true;
|
|
||||||
clearExpressionData();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDBBreakPoint::clearExpressionData()
|
|
||||||
{
|
|
||||||
fileName.clear();
|
|
||||||
condition.clear();
|
|
||||||
funcName.clear();
|
|
||||||
lineNumber = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDebug operator<<(QDebug dbg, const CDBBreakPoint &bp)
|
|
||||||
{
|
|
||||||
QDebug nsp = dbg.nospace();
|
|
||||||
if (!bp.fileName.isEmpty()) {
|
|
||||||
nsp << "fileName='" << bp.fileName << ':' << bp.lineNumber << '\'';
|
|
||||||
} else {
|
|
||||||
nsp << "funcName='" << bp.funcName << '\'';
|
|
||||||
}
|
|
||||||
if (!bp.condition.isEmpty())
|
|
||||||
nsp << " condition='" << bp.condition << '\'';
|
|
||||||
if (bp.ignoreCount)
|
|
||||||
nsp << " ignoreCount=" << bp.ignoreCount;
|
|
||||||
if (bp.enabled)
|
|
||||||
nsp << " enabled";
|
|
||||||
if (bp.oneShot)
|
|
||||||
nsp << " oneShot";
|
|
||||||
return dbg;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString CDBBreakPoint::expression() const
|
|
||||||
{
|
|
||||||
// format the breakpoint expression (file/function and condition)
|
|
||||||
QString rc;
|
|
||||||
QTextStream str(&rc);
|
|
||||||
if (funcName.isEmpty()) {
|
|
||||||
const QChar sourceFileQuote = QLatin1Char(sourceFileQuoteC);
|
|
||||||
str << sourceFileQuote << QDir::toNativeSeparators(fileName) << QLatin1Char(':') << lineNumber << sourceFileQuote;
|
|
||||||
} else {
|
|
||||||
str << funcName;
|
|
||||||
}
|
|
||||||
if (!condition.isEmpty()) {
|
|
||||||
const QChar doubleQuote = QLatin1Char('"');
|
|
||||||
str << QLatin1Char(' ') << doubleQuote << condition << doubleQuote;
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CDBBreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const
|
|
||||||
{
|
|
||||||
const QString expr = expression();
|
|
||||||
if (debugCDB)
|
|
||||||
qDebug() << Q_FUNC_INFO << *this << expr;
|
|
||||||
const HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast<PCWSTR>(expr.utf16()));
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = QString::fromLatin1("Unable to set breakpoint '%1' : %2").
|
|
||||||
arg(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Pass Count is ignoreCount + 1
|
|
||||||
ibp->SetPassCount(ignoreCount + 1u);
|
|
||||||
ULONG flags = 0;
|
|
||||||
if (enabled)
|
|
||||||
flags |= DEBUG_BREAKPOINT_ENABLED;
|
|
||||||
if (oneShot)
|
|
||||||
flags |= DEBUG_BREAKPOINT_ONE_SHOT;
|
|
||||||
ibp->AddFlags(flags);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QString msgCannotAddBreakPoint(const QString &why)
|
|
||||||
{
|
|
||||||
return QString::fromLatin1("Unable to add breakpoint: %1").arg(why);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CDBBreakPoint::add(CIDebugControl* debugControl,
|
|
||||||
QString *errorMessage,
|
|
||||||
unsigned long *id,
|
|
||||||
quint64 *address) const
|
|
||||||
{
|
|
||||||
IDebugBreakpoint2* ibp = 0;
|
|
||||||
if (address)
|
|
||||||
*address = 0;
|
|
||||||
if (id)
|
|
||||||
*id = 0;
|
|
||||||
HRESULT hr = debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &ibp);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("AddBreakpoint2", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!ibp) {
|
|
||||||
*errorMessage = msgCannotAddBreakPoint(QLatin1String("<Unknown error>"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!apply(ibp, errorMessage))
|
|
||||||
return false;
|
|
||||||
// GetOffset can fail when attaching to remote processes, ignore return
|
|
||||||
if (address) {
|
|
||||||
hr = ibp->GetOffset(address);
|
|
||||||
if (FAILED(hr))
|
|
||||||
*address = 0;
|
|
||||||
}
|
|
||||||
if (id) {
|
|
||||||
hr = ibp->GetId(id);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("GetId", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper for normalizing file names:
|
|
||||||
// Map the device paths in a file name to back to drive letters
|
|
||||||
// "/Device/HarddiskVolume1/file.cpp" -> "C:/file.cpp"
|
|
||||||
|
|
||||||
static bool mapDeviceToDriveLetter(QString *s)
|
|
||||||
{
|
|
||||||
enum { bufSize = 512 };
|
|
||||||
// Retrieve drive letters and get their device names.
|
|
||||||
// Do not cache as it may change due to removable/network drives.
|
|
||||||
TCHAR driveLetters[bufSize];
|
|
||||||
if (!GetLogicalDriveStrings(bufSize-1, driveLetters))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
TCHAR driveName[MAX_PATH];
|
|
||||||
TCHAR szDrive[3] = TEXT(" :");
|
|
||||||
for (const TCHAR *driveLetter = driveLetters; *driveLetter; driveLetter++) {
|
|
||||||
szDrive[0] = *driveLetter; // Look up each device name
|
|
||||||
if (QueryDosDevice(szDrive, driveName, MAX_PATH)) {
|
|
||||||
const QString deviceName = QString::fromUtf16(driveName);
|
|
||||||
if (s->startsWith(deviceName)) {
|
|
||||||
s->replace(0, deviceName.size(), QString::fromUtf16(szDrive));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper for normalizing file names:
|
|
||||||
// Determine normalized case of a Windows file name (camelcase.cpp -> CamelCase.cpp)
|
|
||||||
// as the debugger reports lower case file names.
|
|
||||||
// Restriction: File needs to exists and be non-empty and will be to be opened/mapped.
|
|
||||||
// This is the MSDN-recommended way of doing that. The result should be cached.
|
|
||||||
|
|
||||||
static inline QString normalizeFileNameCaseHelper(const QString &f)
|
|
||||||
{
|
|
||||||
HANDLE hFile = CreateFile(f.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
|
||||||
if(hFile == INVALID_HANDLE_VALUE)
|
|
||||||
return f;
|
|
||||||
// Get the file size. We need a non-empty file to map it.
|
|
||||||
DWORD dwFileSizeHi = 0;
|
|
||||||
DWORD dwFileSizeLo = GetFileSize(hFile, &dwFileSizeHi);
|
|
||||||
if (dwFileSizeLo == 0 && dwFileSizeHi == 0) {
|
|
||||||
CloseHandle(hFile);
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
// Create a file mapping object.
|
|
||||||
HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 1, NULL);
|
|
||||||
if (!hFileMap) {
|
|
||||||
CloseHandle(hFile);
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a file mapping to get the file name.
|
|
||||||
void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
|
|
||||||
if (!pMem) {
|
|
||||||
CloseHandle(hFileMap);
|
|
||||||
CloseHandle(hFile);
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString rc;
|
|
||||||
WCHAR pszFilename[MAX_PATH];
|
|
||||||
pszFilename[0] = 0;
|
|
||||||
// Get a file name of the form "/Device/HarddiskVolume1/file.cpp"
|
|
||||||
if (GetMappedFileName (GetCurrentProcess(), pMem, pszFilename, MAX_PATH)) {
|
|
||||||
rc = QString::fromUtf16(pszFilename);
|
|
||||||
if (!mapDeviceToDriveLetter(&rc))
|
|
||||||
rc.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
UnmapViewOfFile(pMem);
|
|
||||||
CloseHandle(hFileMap);
|
|
||||||
CloseHandle(hFile);
|
|
||||||
return rc.isEmpty() ? f : rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure file can be found in editor manager and text markers
|
|
||||||
// Use '/', correct case and capitalize drive letter. Use a cache.
|
|
||||||
|
|
||||||
typedef QHash<QString, QString> NormalizedFileCache;
|
|
||||||
Q_GLOBAL_STATIC(NormalizedFileCache, normalizedFileNameCache)
|
|
||||||
|
|
||||||
QString CDBBreakPoint::normalizeFileName(const QString &f)
|
|
||||||
{
|
|
||||||
QTC_ASSERT(!f.isEmpty(), return f)
|
|
||||||
const NormalizedFileCache::const_iterator it = normalizedFileNameCache()->constFind(f);
|
|
||||||
if (it != normalizedFileNameCache()->constEnd())
|
|
||||||
return it.value();
|
|
||||||
QString normalizedName = QDir::fromNativeSeparators(normalizeFileNameCaseHelper(f));
|
|
||||||
// Upcase drive letter for consistency even if case mapping fails.
|
|
||||||
if (normalizedName.size() > 2 && normalizedName.at(1) == QLatin1Char(':'))
|
|
||||||
normalizedName[0] = normalizedName.at(0).toUpper();
|
|
||||||
normalizedFileNameCache()->insert(f, normalizedName);
|
|
||||||
return normalizedName;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDBBreakPoint::clearNormalizeFileNameCache()
|
|
||||||
{
|
|
||||||
normalizedFileNameCache()->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CDBBreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
WCHAR wszBuf[MAX_PATH];
|
|
||||||
const HRESULT hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint: %1").
|
|
||||||
arg(CdbCore::msgComFailed("GetOffsetExpressionWide", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Pass Count is ignoreCount + 1
|
|
||||||
ibp->GetPassCount(&ignoreCount);
|
|
||||||
if (ignoreCount)
|
|
||||||
ignoreCount--;
|
|
||||||
ULONG flags = 0;
|
|
||||||
ibp->GetFlags(&flags);
|
|
||||||
oneShot = (flags & DEBUG_BREAKPOINT_ONE_SHOT);
|
|
||||||
enabled = (flags & DEBUG_BREAKPOINT_ENABLED);
|
|
||||||
const QString expr = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
|
|
||||||
if (!parseExpression(expr)) {
|
|
||||||
*errorMessage = QString::fromLatin1("Parsing of '%1' failed.").arg(expr);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CDBBreakPoint::parseExpression(const QString &expr)
|
|
||||||
{
|
|
||||||
clearExpressionData();
|
|
||||||
const QChar sourceFileQuote = QLatin1Char(sourceFileQuoteC);
|
|
||||||
// Check for file or function
|
|
||||||
int conditionPos = 0;
|
|
||||||
if (expr.startsWith(sourceFileQuote)) { // `c:\foo.cpp:523`[ "condition"]
|
|
||||||
// Do not fall for the drive letter colon here
|
|
||||||
const int colonPos = expr.indexOf(QLatin1Char(':'), 3);
|
|
||||||
if (colonPos == -1)
|
|
||||||
return false;
|
|
||||||
conditionPos = expr.indexOf(sourceFileQuote, colonPos + 1);
|
|
||||||
if (conditionPos == -1)
|
|
||||||
return false;
|
|
||||||
fileName = normalizeFileName(expr.mid(1, colonPos - 1));
|
|
||||||
const QString lineNumberS = expr.mid(colonPos + 1, conditionPos - colonPos - 1);
|
|
||||||
bool lineNumberOk = false;
|
|
||||||
lineNumber = lineNumberS.toInt(&lineNumberOk);
|
|
||||||
if (!lineNumberOk)
|
|
||||||
return false;
|
|
||||||
conditionPos++;
|
|
||||||
} else {
|
|
||||||
// Check function token
|
|
||||||
conditionPos = expr.indexOf(QLatin1Char(' '));
|
|
||||||
if (conditionPos != -1) {
|
|
||||||
funcName = expr.mid(0, conditionPos);
|
|
||||||
conditionPos++;
|
|
||||||
} else {
|
|
||||||
funcName = expr;
|
|
||||||
conditionPos = expr.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Condition? ".if bla"
|
|
||||||
if (conditionPos >= expr.size())
|
|
||||||
return true;
|
|
||||||
const QChar doubleQuote = QLatin1Char('"');
|
|
||||||
conditionPos = expr.indexOf(doubleQuote, conditionPos);
|
|
||||||
if (conditionPos == -1)
|
|
||||||
return true;
|
|
||||||
conditionPos++;
|
|
||||||
const int condEndPos = expr.lastIndexOf(doubleQuote);
|
|
||||||
if (condEndPos == -1)
|
|
||||||
return false;
|
|
||||||
condition = expr.mid(conditionPos, condEndPos - conditionPos);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CDBBreakPoint::getBreakPointCount(CIDebugControl* debugControl, ULONG *count, QString *errorMessage /* = 0*/)
|
|
||||||
{
|
|
||||||
const HRESULT hr = debugControl->GetNumberBreakpoints(count);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
if (errorMessage)
|
|
||||||
*errorMessage = QString::fromLatin1("Cannot determine breakpoint count: %1").
|
|
||||||
arg(CdbCore::msgComFailed("GetNumberBreakpoints", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CDBBreakPoint::getBreakPoints(CIDebugControl* debugControl, QList<CDBBreakPoint> *bps, QString *errorMessage)
|
|
||||||
{
|
|
||||||
ULONG count = 0;
|
|
||||||
bps->clear();
|
|
||||||
if (!getBreakPointCount(debugControl, &count, errorMessage))
|
|
||||||
return false;
|
|
||||||
// retrieve one by one and parse
|
|
||||||
for (ULONG b= 0; b < count; b++) {
|
|
||||||
IDebugBreakpoint2 *ibp = 0;
|
|
||||||
const HRESULT hr = debugControl->GetBreakpointByIndex2(b, &ibp);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint %1: %2").
|
|
||||||
arg(b).arg(CdbCore::msgComFailed("GetBreakpointByIndex2", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
CDBBreakPoint bp;
|
|
||||||
if (!bp.retrieve(ibp, errorMessage))
|
|
||||||
return false;
|
|
||||||
bps->push_back(bp);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find a breakpoint by id
|
|
||||||
static inline QString msgNoBreakPointWithId(unsigned long id, const QString &why)
|
|
||||||
{
|
|
||||||
return QString::fromLatin1("Unable to find breakpoint with id %1: %2").arg(id).arg(why);
|
|
||||||
}
|
|
||||||
|
|
||||||
static IDebugBreakpoint2 *breakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage)
|
|
||||||
{
|
|
||||||
CIDebugBreakpoint *ibp = 0;
|
|
||||||
const HRESULT hr = ctl->GetBreakpointById2(id, &ibp);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = msgNoBreakPointWithId(id, CdbCore::msgComFailed("GetBreakpointById2", hr));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!ibp) {
|
|
||||||
*errorMessage = msgNoBreakPointWithId(id, QLatin1String("<not found>"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return ibp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove breakpoint by id
|
|
||||||
static bool removeBreakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage)
|
|
||||||
{
|
|
||||||
if (debugBP)
|
|
||||||
qDebug() << Q_FUNC_INFO << id;
|
|
||||||
CIDebugBreakpoint *ibp = breakPointById(ctl, id, errorMessage);
|
|
||||||
if (!ibp)
|
|
||||||
return false;
|
|
||||||
const HRESULT hr = ctl->RemoveBreakpoint2(ibp);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = QString::fromLatin1("Cannot remove breakpoint %1: %2").arg(id).arg(CdbCore::msgComFailed("RemoveBreakpointById2", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set enabled by id
|
|
||||||
|
|
||||||
// Change enabled state of a breakpoint by id
|
|
||||||
static inline QString msgCannotSetBreakPointEnabled(unsigned long id, bool enabled, const QString &why)
|
|
||||||
{
|
|
||||||
return QString::fromLatin1("Cannot %1 breakpoint %2: %3").
|
|
||||||
arg(QLatin1String(enabled ? "enable" : "disable")).arg(id).arg(why);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool enabled, QString *errorMessage)
|
|
||||||
{
|
|
||||||
if (debugBP)
|
|
||||||
qDebug() << Q_FUNC_INFO << id << enabled;
|
|
||||||
CIDebugBreakpoint *ibp = breakPointById(ctl, id, errorMessage);
|
|
||||||
if (!ibp) {
|
|
||||||
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, *errorMessage);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Compare flags
|
|
||||||
ULONG flags;
|
|
||||||
HRESULT hr = ibp->GetFlags(&flags);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, CdbCore::msgComFailed("GetFlags", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const bool wasEnabled = (flags & DEBUG_BREAKPOINT_ENABLED);
|
|
||||||
if (wasEnabled == enabled)
|
|
||||||
return true;
|
|
||||||
// Set new value
|
|
||||||
if (enabled) {
|
|
||||||
flags |= DEBUG_BREAKPOINT_ENABLED;
|
|
||||||
} else {
|
|
||||||
flags &= ~DEBUG_BREAKPOINT_ENABLED;
|
|
||||||
}
|
|
||||||
hr = ibp->SetFlags(flags);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, CdbCore::msgComFailed("SetFlags", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QString msgCannotSetBreakAtFunction(const QString &func, const QString &why)
|
static inline QString msgCannotSetBreakAtFunction(const QString &func, const QString &why)
|
||||||
{
|
{
|
||||||
@@ -512,17 +43,18 @@ static inline QString msgCannotSetBreakAtFunction(const QString &func, const QSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Synchronize (halted) engine breakpoints with those of the BreakHandler.
|
// Synchronize (halted) engine breakpoints with those of the BreakHandler.
|
||||||
bool CDBBreakPoint::synchronizeBreakPoints(CIDebugControl* debugControl,
|
bool synchronizeBreakPoints(CIDebugControl* debugControl,
|
||||||
CIDebugSymbols *syms,
|
CIDebugSymbols *syms,
|
||||||
BreakHandler *handler,
|
BreakHandler *handler,
|
||||||
QString *errorMessage, QStringList *warnings)
|
QString *errorMessage,
|
||||||
|
QStringList *warnings)
|
||||||
{
|
{
|
||||||
errorMessage->clear();
|
errorMessage->clear();
|
||||||
warnings->clear();
|
warnings->clear();
|
||||||
// Do an initial check whether we are in a state that allows
|
// Do an initial check whether we are in a state that allows
|
||||||
// for modifying breakPoints
|
// for modifying breakPoints
|
||||||
ULONG engineCount;
|
ULONG engineCount;
|
||||||
if (!getBreakPointCount(debugControl, &engineCount, errorMessage)) {
|
if (!CdbCore::BreakPoint::getBreakPointCount(debugControl, &engineCount, errorMessage)) {
|
||||||
*errorMessage = QString::fromLatin1("Cannot modify breakpoints: %1").arg(*errorMessage);
|
*errorMessage = QString::fromLatin1("Cannot modify breakpoints: %1").arg(*errorMessage);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -556,7 +88,7 @@ bool CDBBreakPoint::synchronizeBreakPoints(CIDebugControl* debugControl,
|
|||||||
if (breakPointOk) {
|
if (breakPointOk) {
|
||||||
quint64 address;
|
quint64 address;
|
||||||
unsigned long id;
|
unsigned long id;
|
||||||
CDBBreakPoint ncdbbp(*nbd);
|
const CdbCore::BreakPoint ncdbbp = breakPointFromBreakPointData(*nbd);
|
||||||
breakPointOk = ncdbbp.add(debugControl, &warning, &id, &address);
|
breakPointOk = ncdbbp.add(debugControl, &warning, &id, &address);
|
||||||
if (breakPointOk) {
|
if (breakPointOk) {
|
||||||
if (debugBP)
|
if (debugBP)
|
||||||
@@ -578,24 +110,24 @@ bool CDBBreakPoint::synchronizeBreakPoints(CIDebugControl* debugControl,
|
|||||||
warnings->push_back(warning); }
|
warnings->push_back(warning); }
|
||||||
// Delete
|
// Delete
|
||||||
foreach (BreakpointData *rbd, handler->takeRemovedBreakpoints()) {
|
foreach (BreakpointData *rbd, handler->takeRemovedBreakpoints()) {
|
||||||
if (!removeBreakPointById(debugControl, rbd->bpNumber.toUInt(), &warning))
|
if (!CdbCore::BreakPoint::removeBreakPointById(debugControl, rbd->bpNumber.toUInt(), &warning))
|
||||||
warnings->push_back(warning);
|
warnings->push_back(warning);
|
||||||
delete rbd;
|
delete rbd;
|
||||||
}
|
}
|
||||||
// Enable/Disable
|
// Enable/Disable
|
||||||
foreach (BreakpointData *ebd, handler->takeEnabledBreakpoints())
|
foreach (BreakpointData *ebd, handler->takeEnabledBreakpoints())
|
||||||
if (!setBreakPointEnabledById(debugControl, ebd->bpNumber.toUInt(), true, &warning))
|
if (!CdbCore::BreakPoint::setBreakPointEnabledById(debugControl, ebd->bpNumber.toUInt(), true, &warning))
|
||||||
warnings->push_back(warning);
|
warnings->push_back(warning);
|
||||||
foreach (BreakpointData *dbd, handler->takeDisabledBreakpoints())
|
foreach (BreakpointData *dbd, handler->takeDisabledBreakpoints())
|
||||||
if (!setBreakPointEnabledById(debugControl, dbd->bpNumber.toUInt(), false, &warning))
|
if (!CdbCore::BreakPoint::setBreakPointEnabledById(debugControl, dbd->bpNumber.toUInt(), false, &warning))
|
||||||
warnings->push_back(warning);
|
warnings->push_back(warning);
|
||||||
|
|
||||||
if (updateMarkers)
|
if (updateMarkers)
|
||||||
handler->updateMarkers();
|
handler->updateMarkers();
|
||||||
|
|
||||||
if (debugBP > 1) {
|
if (debugBP > 1) {
|
||||||
QList<CDBBreakPoint> bps;
|
QList<CdbCore::BreakPoint> bps;
|
||||||
CDBBreakPoint::getBreakPoints(debugControl, &bps, errorMessage);
|
CdbCore::BreakPoint::getBreakPoints(debugControl, &bps, errorMessage);
|
||||||
qDebug().nospace() << "### Breakpoints in engine: " << bps;
|
qDebug().nospace() << "### Breakpoints in engine: " << bps;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -31,73 +31,39 @@
|
|||||||
#define CDBBREAKPOINTS_H
|
#define CDBBREAKPOINTS_H
|
||||||
|
|
||||||
#include "cdbcom.h"
|
#include "cdbcom.h"
|
||||||
|
#include "breakpoint.h"
|
||||||
|
#include "breakhandler.h"
|
||||||
|
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtCore/QList>
|
#include <QtCore/QList>
|
||||||
|
#include <QtCore/QDir>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QDebug;
|
class QDebug;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
// Convert breakpoint structs
|
||||||
|
inline CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd)
|
||||||
|
{
|
||||||
|
CdbCore::BreakPoint rc;
|
||||||
|
rc.fileName = QDir::toNativeSeparators(bpd.fileName);
|
||||||
|
rc.condition = bpd.condition;
|
||||||
|
rc.funcName = bpd.funcName;
|
||||||
|
rc.ignoreCount = bpd.ignoreCount.isEmpty() ? 0 : bpd.ignoreCount.toInt();
|
||||||
|
rc.lineNumber = bpd.lineNumber.isEmpty() ? -1 : bpd.lineNumber.toInt();
|
||||||
|
rc.oneShot = false;
|
||||||
|
rc.enabled = bpd.enabled;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class BreakHandler;
|
// Synchronize (halted) engine with BreakHandler.
|
||||||
class BreakpointData;
|
bool synchronizeBreakPoints(CIDebugControl* ctl, CIDebugSymbols *syms,
|
||||||
|
BreakHandler *bh,
|
||||||
|
QString *errorMessage, QStringList *warnings);
|
||||||
|
|
||||||
/* CDB Break point data structure with utilities to
|
|
||||||
* apply to engine and to retrieve them from the engine and comparison. */
|
|
||||||
|
|
||||||
struct CDBBreakPoint
|
|
||||||
{
|
|
||||||
CDBBreakPoint();
|
|
||||||
CDBBreakPoint(const BreakpointData &bpd);
|
|
||||||
|
|
||||||
int compare(const CDBBreakPoint& rhs) const;
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
void clearExpressionData();
|
|
||||||
|
|
||||||
QString expression() const;
|
|
||||||
|
|
||||||
// Apply parameters
|
|
||||||
bool apply(IDebugBreakpoint2 *ibp, QString *errorMessage) const;
|
|
||||||
// Convenience to add to a IDebugControl4
|
|
||||||
bool add(CIDebugControl* debugControl,
|
|
||||||
QString *errorMessage,
|
|
||||||
unsigned long *id = 0,
|
|
||||||
quint64 *address = 0) const;
|
|
||||||
|
|
||||||
// Retrieve/parse breakpoints from the interfaces
|
|
||||||
bool retrieve(IDebugBreakpoint2 *ibp, QString *errorMessage);
|
|
||||||
bool parseExpression(const QString &expr);
|
|
||||||
// Retrieve all breakpoints from the engine
|
|
||||||
static bool getBreakPointCount(CIDebugControl* debugControl, ULONG *count, QString *errorMessage = 0);
|
|
||||||
static bool getBreakPoints(CIDebugControl* debugControl, QList<CDBBreakPoint> *bps, QString *errorMessage);
|
|
||||||
// Synchronize (halted) engine with BreakHandler.
|
|
||||||
static bool synchronizeBreakPoints(CIDebugControl* ctl, CIDebugSymbols *syms,
|
|
||||||
BreakHandler *bh,
|
|
||||||
QString *errorMessage, QStringList *warnings);
|
|
||||||
|
|
||||||
// Return a 'canonical' file (using '/' and capitalized drive letter)
|
|
||||||
static QString normalizeFileName(const QString &f);
|
|
||||||
static void clearNormalizeFileNameCache();
|
|
||||||
|
|
||||||
QString fileName; // short name of source file
|
|
||||||
QString condition; // condition associated with breakpoint
|
|
||||||
unsigned long ignoreCount; // ignore count associated with breakpoint
|
|
||||||
int lineNumber; // line in source file
|
|
||||||
QString funcName; // name of containing function
|
|
||||||
bool oneShot;
|
|
||||||
bool enabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
QDebug operator<<(QDebug, const CDBBreakPoint &bp);
|
|
||||||
|
|
||||||
inline bool operator==(const CDBBreakPoint& b1, const CDBBreakPoint& b2)
|
|
||||||
{ return b1.compare(b2) == 0; }
|
|
||||||
inline bool operator!=(const CDBBreakPoint& b1, const CDBBreakPoint& b2)
|
|
||||||
{ return b1.compare(b2) != 0; }
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|
||||||
|
|||||||
@@ -26,15 +26,24 @@ HEADERS += \
|
|||||||
$$PWD/cdbcom.h \
|
$$PWD/cdbcom.h \
|
||||||
$$PWD/coreengine.h \
|
$$PWD/coreengine.h \
|
||||||
$$PWD/debugoutputbase.h \
|
$$PWD/debugoutputbase.h \
|
||||||
$$PWD/debugeventcallbackbase.h
|
$$PWD/debugeventcallbackbase.h \
|
||||||
|
$$PWD/symbolgroupcontext.h \
|
||||||
|
$$PWD/stacktracecontext.h \
|
||||||
|
$$PWD/breakpoint.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/coreengine.cpp \
|
$$PWD/coreengine.cpp \
|
||||||
$$PWD/debugoutputbase.cpp \
|
$$PWD/debugoutputbase.cpp \
|
||||||
$$PWD/debugeventcallbackbase.cpp
|
$$PWD/debugeventcallbackbase.cpp \
|
||||||
|
$$PWD/symbolgroupcontext.cpp \
|
||||||
|
$$PWD/stacktracecontext.cpp \
|
||||||
|
$$PWD/breakpoint.cpp
|
||||||
|
|
||||||
INCLUDEPATH*=$$PWD
|
INCLUDEPATH*=$$PWD
|
||||||
DEPENDPATH*=$$PWD
|
DEPENDPATH*=$$PWD
|
||||||
|
|
||||||
|
LIBS+=-lpsapi
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
message("Debugging Tools for Windows could not be found in $$CDB_PATH")
|
message("Debugging Tools for Windows could not be found in $$CDB_PATH")
|
||||||
CDB_PATH=""
|
CDB_PATH=""
|
||||||
|
|||||||
@@ -248,17 +248,21 @@ QString CdbDebugEngine::editorToolTip(const QString &exp, const QString &functio
|
|||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
QString rc;
|
QString rc;
|
||||||
// Find the frame of the function if there is any
|
// Find the frame of the function if there is any
|
||||||
CdbStackFrameContext *frame = 0;
|
CdbSymbolGroupContext *frame = 0;
|
||||||
if (m_d->m_currentStackTrace && !function.isEmpty()) {
|
if (m_d->m_currentStackTrace && !function.isEmpty()) {
|
||||||
const int frameIndex = m_d->m_currentStackTrace->indexOf(function);
|
const int frameIndex = m_d->m_currentStackTrace->indexOf(function);
|
||||||
|
if (debugToolTips)
|
||||||
|
qDebug() << "CdbDebugEngine::editorToolTip" << exp << function << frameIndex;
|
||||||
if (frameIndex != -1)
|
if (frameIndex != -1)
|
||||||
frame = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
|
frame = m_d->m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, &errorMessage);
|
||||||
}
|
}
|
||||||
if (frame && frame->editorToolTip(QLatin1String("local.") + exp, &rc, &errorMessage))
|
if (frame && frame->editorToolTip(QLatin1String("local.") + exp, &rc, &errorMessage))
|
||||||
return rc;
|
return rc;
|
||||||
// No function/symbol context found, try to evaluate in current context.
|
// No function/symbol context found, try to evaluate in current context.
|
||||||
// Do not append type as this will mostly be 'long long' for integers, etc.
|
// Do not append type as this will mostly be 'long long' for integers, etc.
|
||||||
QString type;
|
QString type;
|
||||||
|
if (debugToolTips)
|
||||||
|
qDebug() << "Defaulting to expression";
|
||||||
if (!m_d->evaluateExpression(exp, &rc, &type, &errorMessage))
|
if (!m_d->evaluateExpression(exp, &rc, &type, &errorMessage))
|
||||||
return QString();
|
return QString();
|
||||||
return rc;
|
return rc;
|
||||||
@@ -341,7 +345,7 @@ void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters>
|
|||||||
{
|
{
|
||||||
if (debugCDBExecution)
|
if (debugCDBExecution)
|
||||||
qDebug() << "startDebugger" << *sp;
|
qDebug() << "startDebugger" << *sp;
|
||||||
CDBBreakPoint::clearNormalizeFileNameCache();
|
CdbCore::BreakPoint::clearNormalizeFileNameCache();
|
||||||
setState(AdapterStarting, Q_FUNC_INFO, __LINE__);
|
setState(AdapterStarting, Q_FUNC_INFO, __LINE__);
|
||||||
m_d->checkVersion();
|
m_d->checkVersion();
|
||||||
if (m_d->m_hDebuggeeProcess) {
|
if (m_d->m_hDebuggeeProcess) {
|
||||||
@@ -599,13 +603,13 @@ void CdbDebugEngine::detachDebugger()
|
|||||||
m_d->endDebugging(CdbDebugEnginePrivate::EndDebuggingDetach);
|
m_d->endDebugging(CdbDebugEnginePrivate::EndDebuggingDetach);
|
||||||
}
|
}
|
||||||
|
|
||||||
CdbStackFrameContext *CdbDebugEnginePrivate::getStackFrameContext(int frameIndex, QString *errorMessage) const
|
CdbSymbolGroupContext *CdbDebugEnginePrivate::getSymbolGroupContext(int frameIndex, QString *errorMessage) const
|
||||||
{
|
{
|
||||||
if (!m_currentStackTrace) {
|
if (!m_currentStackTrace) {
|
||||||
*errorMessage = QLatin1String(msgNoStackTraceC);
|
*errorMessage = QLatin1String(msgNoStackTraceC);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (CdbStackFrameContext *sg = m_currentStackTrace->frameContextAt(frameIndex, errorMessage))
|
if (CdbSymbolGroupContext *sg = m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, errorMessage))
|
||||||
return sg;
|
return sg;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -649,7 +653,7 @@ void CdbDebugEngine::updateWatchData(const WatchData &incomplete)
|
|||||||
bool success = false;
|
bool success = false;
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
do {
|
do {
|
||||||
CdbStackFrameContext *sg = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
|
CdbSymbolGroupContext *sg = m_d->m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, &errorMessage);
|
||||||
if (!sg)
|
if (!sg)
|
||||||
break;
|
break;
|
||||||
if (!sg->completeData(incomplete, watchHandler, &errorMessage))
|
if (!sg->completeData(incomplete, watchHandler, &errorMessage))
|
||||||
@@ -902,7 +906,7 @@ void CdbDebugEngine::runToLineExec(const QString &fileName, int lineNumber)
|
|||||||
{
|
{
|
||||||
manager()->showDebuggerOutput(LogMisc, tr("Running up to %1:%2...").arg(fileName).arg(lineNumber));
|
manager()->showDebuggerOutput(LogMisc, tr("Running up to %1:%2...").arg(fileName).arg(lineNumber));
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
CDBBreakPoint tempBreakPoint;
|
CdbCore::BreakPoint tempBreakPoint;
|
||||||
tempBreakPoint.fileName = fileName;
|
tempBreakPoint.fileName = fileName;
|
||||||
tempBreakPoint.lineNumber = lineNumber;
|
tempBreakPoint.lineNumber = lineNumber;
|
||||||
tempBreakPoint.oneShot = true;
|
tempBreakPoint.oneShot = true;
|
||||||
@@ -916,7 +920,7 @@ void CdbDebugEngine::runToFunctionExec(const QString &functionName)
|
|||||||
{
|
{
|
||||||
manager()->showDebuggerOutput(LogMisc, tr("Running up to function '%1()'...").arg(functionName));
|
manager()->showDebuggerOutput(LogMisc, tr("Running up to function '%1()'...").arg(functionName));
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
CDBBreakPoint tempBreakPoint;
|
CdbCore::BreakPoint tempBreakPoint;
|
||||||
tempBreakPoint.funcName = functionName;
|
tempBreakPoint.funcName = functionName;
|
||||||
tempBreakPoint.oneShot = true;
|
tempBreakPoint.oneShot = true;
|
||||||
const bool ok = tempBreakPoint.add(m_d->interfaces().debugControl, &errorMessage)
|
const bool ok = tempBreakPoint.add(m_d->interfaces().debugControl, &errorMessage)
|
||||||
@@ -939,7 +943,7 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v
|
|||||||
bool success = false;
|
bool success = false;
|
||||||
do {
|
do {
|
||||||
QString newValue;
|
QString newValue;
|
||||||
CdbStackFrameContext *sg = m_d->getStackFrameContext(frameIndex, &errorMessage);
|
CdbSymbolGroupContext *sg = m_d->getSymbolGroupContext(frameIndex, &errorMessage);
|
||||||
if (!sg)
|
if (!sg)
|
||||||
break;
|
break;
|
||||||
if (!sg->assignValue(expr, value, &newValue, &errorMessage))
|
if (!sg->assignValue(expr, value, &newValue, &errorMessage))
|
||||||
@@ -1004,7 +1008,7 @@ void CdbDebugEngine::activateFrame(int frameIndex)
|
|||||||
|
|
||||||
if (oldIndex != frameIndex || m_d->m_firstActivatedFrame) {
|
if (oldIndex != frameIndex || m_d->m_firstActivatedFrame) {
|
||||||
watchHandler->beginCycle();
|
watchHandler->beginCycle();
|
||||||
if (CdbStackFrameContext *sgc = m_d->getStackFrameContext(frameIndex, &errorMessage))
|
if (CdbSymbolGroupContext *sgc = m_d->getSymbolGroupContext(frameIndex, &errorMessage))
|
||||||
success = sgc->populateModelInitially(watchHandler, &errorMessage);
|
success = sgc->populateModelInitially(watchHandler, &errorMessage);
|
||||||
watchHandler->endCycle();
|
watchHandler->endCycle();
|
||||||
} else {
|
} else {
|
||||||
@@ -1059,7 +1063,7 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa
|
|||||||
// called again from the debug event handler.
|
// called again from the debug event handler.
|
||||||
|
|
||||||
ULONG dummy;
|
ULONG dummy;
|
||||||
const bool wasRunning = !CDBBreakPoint::getBreakPointCount(interfaces().debugControl, &dummy);
|
const bool wasRunning = !CdbCore::BreakPoint::getBreakPointCount(interfaces().debugControl, &dummy);
|
||||||
if (debugCDB)
|
if (debugCDB)
|
||||||
qDebug() << Q_FUNC_INFO << "\n Running=" << wasRunning;
|
qDebug() << Q_FUNC_INFO << "\n Running=" << wasRunning;
|
||||||
|
|
||||||
@@ -1074,10 +1078,10 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa
|
|||||||
}
|
}
|
||||||
|
|
||||||
QStringList warnings;
|
QStringList warnings;
|
||||||
const bool ok = CDBBreakPoint::synchronizeBreakPoints(interfaces().debugControl,
|
const bool ok = synchronizeBreakPoints(interfaces().debugControl,
|
||||||
interfaces().debugSymbols,
|
interfaces().debugSymbols,
|
||||||
manager()->breakHandler(),
|
manager()->breakHandler(),
|
||||||
errorMessage, &warnings);
|
errorMessage, &warnings);
|
||||||
if (const int warningsCount = warnings.size())
|
if (const int warningsCount = warnings.size())
|
||||||
for (int w = 0; w < warningsCount; w++)
|
for (int w = 0; w < warningsCount; w++)
|
||||||
m_engine->warning(warnings.at(w));
|
m_engine->warning(warnings.at(w));
|
||||||
@@ -1361,7 +1365,7 @@ ULONG CdbDebugEnginePrivate::updateThreadList()
|
|||||||
ULONG currentThreadId;
|
ULONG currentThreadId;
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
// When interrupting, an artifical thread with a breakpoint is created.
|
// When interrupting, an artifical thread with a breakpoint is created.
|
||||||
if (!CdbStackTraceContext::getThreads(interfaces(), true, &threads, ¤tThreadId, &errorMessage))
|
if (!CdbStackTraceContext::getThreads(interfaces(), &threads, ¤tThreadId, &errorMessage))
|
||||||
m_engine->warning(errorMessage);
|
m_engine->warning(errorMessage);
|
||||||
manager()->threadsHandler()->setThreads(threads);
|
manager()->threadsHandler()->setThreads(threads);
|
||||||
return currentThreadId;
|
return currentThreadId;
|
||||||
@@ -1378,14 +1382,18 @@ static inline unsigned long dumperThreadId(const QList<StackFrame> &frames,
|
|||||||
{
|
{
|
||||||
if (frames.empty())
|
if (frames.empty())
|
||||||
return CdbDumperHelper::InvalidDumperCallThread;
|
return CdbDumperHelper::InvalidDumperCallThread;
|
||||||
if (frames.at(0).function == QLatin1String(CdbStackTraceContext::winFuncDebugBreakPoint))
|
switch (CdbCore::StackTraceContext::specialFunction(frames.at(0).from, frames.at(0).function)) {
|
||||||
|
case CdbCore::StackTraceContext::BreakPointFunction:
|
||||||
|
case CdbCore::StackTraceContext::WaitFunction:
|
||||||
return CdbDumperHelper::InvalidDumperCallThread;
|
return CdbDumperHelper::InvalidDumperCallThread;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Check remaining frames for wait
|
||||||
const int waitCheckDepth = qMin(frames.size(), 5);
|
const int waitCheckDepth = qMin(frames.size(), 5);
|
||||||
static const QString waitForPrefix = QLatin1String(CdbStackTraceContext::winFuncWaitForPrefix);
|
for (int f = 1; f < waitCheckDepth; f++) {
|
||||||
static const QString msgWaitForPrefix = QLatin1String(CdbStackTraceContext::winFuncMsgWaitForPrefix);
|
if (CdbCore::StackTraceContext::specialFunction(frames.at(f).from, frames.at(f).function)
|
||||||
for (int f = 0; f < waitCheckDepth; f++) {
|
== CdbCore::StackTraceContext::WaitFunction)
|
||||||
const QString &function = frames.at(f).function;
|
|
||||||
if (function.startsWith(waitForPrefix) || function.startsWith(msgWaitForPrefix))
|
|
||||||
return CdbDumperHelper::InvalidDumperCallThread;
|
return CdbDumperHelper::InvalidDumperCallThread;
|
||||||
}
|
}
|
||||||
return currentThread;
|
return currentThread;
|
||||||
@@ -1404,7 +1412,7 @@ void CdbDebugEnginePrivate::updateStackTrace()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_currentStackTrace =
|
m_currentStackTrace =
|
||||||
CdbStackTraceContext::create(m_dumper, m_currentThreadId, &errorMessage);
|
CdbStackTraceContext::create(m_dumper, &errorMessage);
|
||||||
if (!m_currentStackTrace) {
|
if (!m_currentStackTrace) {
|
||||||
m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
|
m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
|
||||||
return;
|
return;
|
||||||
@@ -1413,7 +1421,7 @@ void CdbDebugEnginePrivate::updateStackTrace()
|
|||||||
#if 0
|
#if 0
|
||||||
m_engine->reloadDisassembler(); // requires stack trace
|
m_engine->reloadDisassembler(); // requires stack trace
|
||||||
#endif
|
#endif
|
||||||
const QList<StackFrame> stackFrames = m_currentStackTrace->frames();
|
const QList<StackFrame> stackFrames = m_currentStackTrace->stackFrames();
|
||||||
// find the first usable frame and select it
|
// find the first usable frame and select it
|
||||||
int current = -1;
|
int current = -1;
|
||||||
const int count = stackFrames.count();
|
const int count = stackFrames.count();
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ class DebuggerManager;
|
|||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class WatchHandler;
|
class WatchHandler;
|
||||||
class CdbStackFrameContext;
|
|
||||||
class CdbStackTraceContext;
|
class CdbStackTraceContext;
|
||||||
|
class CdbSymbolGroupContext;
|
||||||
|
|
||||||
class CdbDebugEnginePrivate : public CdbCore::CoreEngine
|
class CdbDebugEnginePrivate : public CdbCore::CoreEngine
|
||||||
{
|
{
|
||||||
@@ -82,7 +82,7 @@ public:
|
|||||||
void cleanStackTrace();
|
void cleanStackTrace();
|
||||||
void clearForRun();
|
void clearForRun();
|
||||||
void handleModuleLoad(const QString &);
|
void handleModuleLoad(const QString &);
|
||||||
CdbStackFrameContext *getStackFrameContext(int frameIndex, QString *errorMessage) const;
|
CdbSymbolGroupContext *getSymbolGroupContext(int frameIndex, QString *errorMessage) const;
|
||||||
void clearDisplay();
|
void clearDisplay();
|
||||||
|
|
||||||
bool interruptInterferiorProcess(QString *errorMessage);
|
bool interruptInterferiorProcess(QString *errorMessage);
|
||||||
@@ -135,6 +135,7 @@ enum { messageTimeOut = 5000 };
|
|||||||
enum { debugCDB = 0 };
|
enum { debugCDB = 0 };
|
||||||
enum { debugCDBExecution = 0 };
|
enum { debugCDBExecution = 0 };
|
||||||
enum { debugCDBWatchHandling = 0 };
|
enum { debugCDBWatchHandling = 0 };
|
||||||
|
enum { debugToolTips = 0 };
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
|
|||||||
QString msg;
|
QString msg;
|
||||||
{
|
{
|
||||||
QTextStream str(&msg);
|
QTextStream str(&msg);
|
||||||
formatException(Exception, m_pEngine->m_d->m_dumper, str);
|
formatException(Exception, &m_pEngine->m_d->interfaces(), str);
|
||||||
}
|
}
|
||||||
const bool fatal = isFatalException(Exception->ExceptionCode);
|
const bool fatal = isFatalException(Exception->ExceptionCode);
|
||||||
if (debugCDB)
|
if (debugCDB)
|
||||||
|
|||||||
@@ -29,8 +29,7 @@
|
|||||||
|
|
||||||
#include "cdbexceptionutils.h"
|
#include "cdbexceptionutils.h"
|
||||||
#include "cdbdebugengine_p.h"
|
#include "cdbdebugengine_p.h"
|
||||||
#include "cdbdumperhelper.h"
|
#include "stacktracecontext.h"
|
||||||
#include "cdbstacktracecontext.h"
|
|
||||||
|
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtCore/QTextStream>
|
#include <QtCore/QTextStream>
|
||||||
@@ -247,15 +246,13 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
|
|||||||
|
|
||||||
// Format exception with stacktrace in case of C++ exception
|
// Format exception with stacktrace in case of C++ exception
|
||||||
void formatException(const EXCEPTION_RECORD64 *e,
|
void formatException(const EXCEPTION_RECORD64 *e,
|
||||||
const QSharedPointer<CdbDumperHelper> &dumper,
|
const CdbCore::ComInterfaces *cif,
|
||||||
QTextStream &str)
|
QTextStream &str)
|
||||||
{
|
{
|
||||||
formatException(e, str);
|
formatException(e, str);
|
||||||
if (e->ExceptionCode == winExceptionCppException) {
|
if (e->ExceptionCode == winExceptionCppException) {
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
ULONG currentThreadId = 0;
|
if (CdbCore::StackTraceContext *stc = CdbCore::StackTraceContext::create(cif, 9999, &errorMessage)) {
|
||||||
dumper->comInterfaces()->debugSystemObjects->GetCurrentThreadId(¤tThreadId);
|
|
||||||
if (CdbStackTraceContext *stc = CdbStackTraceContext::create(dumper, currentThreadId, &errorMessage)) {
|
|
||||||
str << "at:\n";
|
str << "at:\n";
|
||||||
stc->format(str);
|
stc->format(str);
|
||||||
str <<'\n';
|
str <<'\n';
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ QT_BEGIN_NAMESPACE
|
|||||||
class QTextStream;
|
class QTextStream;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace CdbCore {
|
||||||
|
struct ComInterfaces;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -94,7 +98,7 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str);
|
|||||||
|
|
||||||
// Format exception with stacktrace in case of C++ exception
|
// Format exception with stacktrace in case of C++ exception
|
||||||
void formatException(const EXCEPTION_RECORD64 *e,
|
void formatException(const EXCEPTION_RECORD64 *e,
|
||||||
const QSharedPointer<CdbDumperHelper> &dumper,
|
const CdbCore::ComInterfaces *cif,
|
||||||
QTextStream &str);
|
QTextStream &str);
|
||||||
|
|
||||||
// Is this a crash/recoverable?
|
// Is this a crash/recoverable?
|
||||||
|
|||||||
@@ -310,15 +310,14 @@ bool CdbStackFrameContext::populateModelInitially(WatchHandler *wh, QString *err
|
|||||||
qDebug() << "populateModelInitially dumpers=" << m_useDumpers;
|
qDebug() << "populateModelInitially dumpers=" << m_useDumpers;
|
||||||
// Recurse down items that are initially expanded in the view, stop processing for
|
// Recurse down items that are initially expanded in the view, stop processing for
|
||||||
// dumper items.
|
// dumper items.
|
||||||
const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper,
|
const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper);
|
||||||
m_dumper->comInterfaces()->debugDataSpaces);
|
|
||||||
const bool rc = m_useDumpers ?
|
const bool rc = m_useDumpers ?
|
||||||
CdbSymbolGroupContext::populateModelInitially(rctx,
|
CdbSymbolGroupContext::populateModelInitiallyHelper(rctx,
|
||||||
WatchHandleDumperInserter(wh, m_dumper),
|
WatchHandleDumperInserter(wh, m_dumper),
|
||||||
WatchHandlerExpandedPredicate(wh),
|
WatchHandlerExpandedPredicate(wh),
|
||||||
isDumperPredicate,
|
isDumperPredicate,
|
||||||
errorMessage) :
|
errorMessage) :
|
||||||
CdbSymbolGroupContext::populateModelInitially(rctx,
|
CdbSymbolGroupContext::populateModelInitiallyHelper(rctx,
|
||||||
WatchHandlerModelInserter(wh),
|
WatchHandlerModelInserter(wh),
|
||||||
WatchHandlerExpandedPredicate(wh),
|
WatchHandlerExpandedPredicate(wh),
|
||||||
falsePredicate,
|
falsePredicate,
|
||||||
@@ -333,11 +332,10 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal,
|
|||||||
if (debugCDBWatchHandling)
|
if (debugCDBWatchHandling)
|
||||||
qDebug() << ">completeData src=" << incompleteLocal.source << incompleteLocal.toString();
|
qDebug() << ">completeData src=" << incompleteLocal.source << incompleteLocal.toString();
|
||||||
|
|
||||||
const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper,
|
const CdbSymbolGroupRecursionContext rctx(m_symbolContext, OwnerSymbolGroupDumper);
|
||||||
m_dumper->comInterfaces()->debugDataSpaces);
|
|
||||||
// Expand symbol group items, recurse one level from desired item
|
// Expand symbol group items, recurse one level from desired item
|
||||||
if (!m_useDumpers) {
|
if (!m_useDumpers) {
|
||||||
return CdbSymbolGroupContext::completeData(rctx, incompleteLocal,
|
return CdbSymbolGroupContext::completeDataHelper(rctx, incompleteLocal,
|
||||||
WatchHandlerModelInserter(wh),
|
WatchHandlerModelInserter(wh),
|
||||||
MatchINamePredicate(incompleteLocal.iname),
|
MatchINamePredicate(incompleteLocal.iname),
|
||||||
falsePredicate,
|
falsePredicate,
|
||||||
@@ -375,7 +373,7 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Expand symbol group items, recurse one level from desired item
|
// Expand symbol group items, recurse one level from desired item
|
||||||
return CdbSymbolGroupContext::completeData(rctx, incompleteLocal,
|
return CdbSymbolGroupContext::completeDataHelper(rctx, incompleteLocal,
|
||||||
WatchHandleDumperInserter(wh, m_dumper),
|
WatchHandleDumperInserter(wh, m_dumper),
|
||||||
MatchINamePredicate(incompleteLocal.iname),
|
MatchINamePredicate(incompleteLocal.iname),
|
||||||
isDumperPredicate,
|
isDumperPredicate,
|
||||||
@@ -398,8 +396,12 @@ bool CdbStackFrameContext::editorToolTip(const QString &iname,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Check dumpers. Should actually be just one item.
|
// Check dumpers. Should actually be just one item.
|
||||||
const WatchData wd = m_symbolContext->watchDataAt(index);
|
|
||||||
if (m_useDumpers && !wd.error && m_dumper->state() != CdbDumperHelper::Disabled) {
|
WatchData wd;
|
||||||
|
const unsigned rc = m_symbolContext->watchDataAt(index, &wd);
|
||||||
|
if (m_useDumpers && !wd.error
|
||||||
|
&& (0u == (rc & CdbCore::SymbolGroupContext::InternalDumperMask))
|
||||||
|
&& m_dumper->state() != CdbDumperHelper::Disabled) {
|
||||||
QList<WatchData> result;
|
QList<WatchData> result;
|
||||||
if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, &result, errorMessage)) {
|
if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, &result, errorMessage)) {
|
||||||
foreach (const WatchData &dwd, result) {
|
foreach (const WatchData &dwd, result) {
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -28,332 +28,110 @@
|
|||||||
**************************************************************************/
|
**************************************************************************/
|
||||||
|
|
||||||
#include "cdbstacktracecontext.h"
|
#include "cdbstacktracecontext.h"
|
||||||
#include "coreengine.h"
|
|
||||||
#include "cdbstackframecontext.h"
|
|
||||||
#include "cdbbreakpoint.h"
|
|
||||||
#include "cdbsymbolgroupcontext.h"
|
#include "cdbsymbolgroupcontext.h"
|
||||||
#include "cdbdebugengine_p.h"
|
|
||||||
#include "cdbdumperhelper.h"
|
#include "cdbdumperhelper.h"
|
||||||
|
#include "cdbdebugengine_p.h"
|
||||||
#include "debuggeractions.h"
|
#include "debuggeractions.h"
|
||||||
#include "debuggermanager.h"
|
#include "watchutils.h"
|
||||||
|
|
||||||
#include <QtCore/QDir>
|
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
#include <QtCore/QTextStream>
|
|
||||||
|
enum { debug = 1 };
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
const char *CdbStackTraceContext::winFuncFastSystemCallRet = "ntdll!KiFastSystemCallRet";
|
|
||||||
const char *CdbStackTraceContext::winFuncDebugBreakPoint = "ntdll!DbgBreakPoint";
|
|
||||||
const char *CdbStackTraceContext::winFuncWaitForPrefix = "kernel32!WaitFor";
|
|
||||||
const char *CdbStackTraceContext::winFuncMsgWaitForPrefix = "kernel32!MsgWaitForMultipleObjects";
|
|
||||||
|
|
||||||
CdbStackTraceContext::CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper) :
|
CdbStackTraceContext::CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper) :
|
||||||
m_dumper(dumper),
|
CdbCore::StackTraceContext(dumper->comInterfaces()),
|
||||||
m_cif(dumper->comInterfaces()),
|
m_dumper(dumper)
|
||||||
m_instructionOffset(0)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CdbStackTraceContext *CdbStackTraceContext::create(const QSharedPointer<CdbDumperHelper> &dumper,
|
CdbStackTraceContext *CdbStackTraceContext::create(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||||
unsigned long threadId,
|
|
||||||
QString *errorMessage)
|
QString *errorMessage)
|
||||||
{
|
{
|
||||||
if (debugCDB)
|
|
||||||
qDebug() << Q_FUNC_INFO << threadId;
|
|
||||||
// fill the DEBUG_STACK_FRAME array
|
|
||||||
ULONG frameCount;
|
|
||||||
CdbStackTraceContext *ctx = new CdbStackTraceContext(dumper);
|
CdbStackTraceContext *ctx = new CdbStackTraceContext(dumper);
|
||||||
const HRESULT hr = dumper->comInterfaces()->debugControl->GetStackTrace(0, 0, 0, ctx->m_cdbFrames, CdbStackTraceContext::maxFrames, &frameCount);
|
if (!ctx->init(UINT_MAX, errorMessage)) {
|
||||||
if (FAILED(hr)) {
|
|
||||||
delete ctx;
|
|
||||||
*errorMessage = CdbCore::msgComFailed("GetStackTrace", hr);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!ctx->init(frameCount, errorMessage)) {
|
|
||||||
delete ctx;
|
delete ctx;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
CdbStackTraceContext::~CdbStackTraceContext()
|
CdbCore::SymbolGroupContext
|
||||||
|
*CdbStackTraceContext::createSymbolGroup(const CdbCore::ComInterfaces & /* cif */,
|
||||||
|
int index,
|
||||||
|
const QString &prefix,
|
||||||
|
CIDebugSymbolGroup *comSymbolGroup,
|
||||||
|
QString *errorMessage)
|
||||||
{
|
{
|
||||||
qDeleteAll(m_frameContexts);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessage*/)
|
|
||||||
{
|
|
||||||
if (debugCDB)
|
|
||||||
qDebug() << Q_FUNC_INFO << frameCount;
|
|
||||||
|
|
||||||
const QChar exclamationMark = QLatin1Char('!');
|
|
||||||
m_frameContexts.resize(frameCount);
|
|
||||||
qFill(m_frameContexts, static_cast<CdbStackFrameContext*>(0));
|
|
||||||
|
|
||||||
// Convert the DEBUG_STACK_FRAMEs to our StackFrame structure and populate the frames
|
|
||||||
WCHAR wszBuf[MAX_PATH];
|
|
||||||
for (ULONG i=0; i < frameCount; ++i) {
|
|
||||||
StackFrame frame;
|
|
||||||
frame.level = i;
|
|
||||||
const ULONG64 instructionOffset = m_cdbFrames[i].InstructionOffset;
|
|
||||||
if (i == 0)
|
|
||||||
m_instructionOffset = instructionOffset;
|
|
||||||
frame.address = QString::fromLatin1("0x%1").arg(instructionOffset, 0, 16);
|
|
||||||
|
|
||||||
m_cif->debugSymbols->GetNameByOffsetWide(instructionOffset, wszBuf, MAX_PATH, 0, 0);
|
|
||||||
// Determine function and module, if available
|
|
||||||
frame.function = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
|
|
||||||
const int moduleSepPos = frame.function.indexOf(exclamationMark);
|
|
||||||
if (moduleSepPos != -1)
|
|
||||||
frame.from = frame.function.mid(0, moduleSepPos);
|
|
||||||
|
|
||||||
ULONG ulLine;
|
|
||||||
ULONG64 ul64Displacement;
|
|
||||||
const HRESULT hr = m_cif->debugSymbols->GetLineByOffsetWide(instructionOffset, &ulLine, wszBuf, MAX_PATH, 0, &ul64Displacement);
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
frame.line = ulLine;
|
|
||||||
// Vitally important to use canonical file that matches editormanager,
|
|
||||||
// else the marker will not show.
|
|
||||||
frame.file = CDBBreakPoint::normalizeFileName(QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)));
|
|
||||||
}
|
|
||||||
m_frames.push_back(frame);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CdbStackTraceContext::indexOf(const QString &function) const
|
|
||||||
{
|
|
||||||
|
|
||||||
const QChar exclamationMark = QLatin1Char('!');
|
|
||||||
const int count = m_frames.size();
|
|
||||||
// Module contained ('module!foo'). Exact match
|
|
||||||
if (function.contains(exclamationMark)) {
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
if (m_frames.at(i).function == function)
|
|
||||||
return i;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
// No module, fuzzy match
|
|
||||||
QString pattern = exclamationMark + function;
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
if (m_frames.at(i).function.endsWith(pattern))
|
|
||||||
return i;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QString msgFrameContextFailed(int index, const StackFrame &f, const QString &why)
|
|
||||||
{
|
|
||||||
return QString::fromLatin1("Unable to create stack frame context #%1, %2:%3 (%4): %5").
|
|
||||||
arg(index).arg(f.function).arg(f.line).arg(f.file, why);
|
|
||||||
}
|
|
||||||
|
|
||||||
CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *errorMessage)
|
|
||||||
{
|
|
||||||
// Create a frame on demand
|
|
||||||
if (debugCDB)
|
|
||||||
qDebug() << Q_FUNC_INFO << index;
|
|
||||||
|
|
||||||
if (index < 0 || index >= m_frameContexts.size()) {
|
|
||||||
*errorMessage = QString::fromLatin1("%1: Index %2 out of range %3.").
|
|
||||||
arg(QLatin1String(Q_FUNC_INFO)).arg(index).arg(m_frameContexts.size());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (m_frameContexts.at(index))
|
|
||||||
return m_frameContexts.at(index);
|
|
||||||
CIDebugSymbolGroup *sg = createSymbolGroup(index, errorMessage);
|
|
||||||
if (!sg) {
|
|
||||||
*errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Exclude uninitialized variables if desired
|
// Exclude uninitialized variables if desired
|
||||||
QStringList uninitializedVariables;
|
QStringList uninitializedVariables;
|
||||||
if (theDebuggerAction(UseCodeModel)->isChecked()) {
|
const CdbCore::StackFrame &frame = stackFrameAt(index);
|
||||||
const StackFrame &frame = m_frames.at(index);
|
if (theDebuggerAction(UseCodeModel)->isChecked())
|
||||||
getUninitializedVariables(DebuggerManager::instance()->cppCodeModelSnapshot(), frame.function, frame.file, frame.line, &uninitializedVariables);
|
getUninitializedVariables(DebuggerManager::instance()->cppCodeModelSnapshot(), frame.function, frame.fileName, frame.line, &uninitializedVariables);
|
||||||
}
|
if (debug)
|
||||||
CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(QLatin1String("local"), sg, uninitializedVariables, errorMessage);
|
qDebug() << frame << uninitializedVariables;
|
||||||
|
CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(prefix,
|
||||||
|
comSymbolGroup,
|
||||||
|
m_dumper,
|
||||||
|
uninitializedVariables,
|
||||||
|
errorMessage);
|
||||||
if (!sc) {
|
if (!sc) {
|
||||||
*errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage);
|
*errorMessage = msgFrameContextFailed(index, frame, *errorMessage);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
CdbStackFrameContext *fr = new CdbStackFrameContext(m_dumper, sc);
|
return sc;
|
||||||
m_frameContexts[index] = fr;
|
|
||||||
return fr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *errorMessage)
|
CdbSymbolGroupContext *CdbStackTraceContext::cdbSymbolGroupContextAt(int index, QString *errorMessage)
|
||||||
{
|
{
|
||||||
CIDebugSymbolGroup *sg = 0;
|
return static_cast<CdbSymbolGroupContext *>(symbolGroupContextAt(index, errorMessage));
|
||||||
HRESULT hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &sg);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = CdbCore::msgComFailed("GetScopeSymbolGroup", hr);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = m_cif->debugSymbols->SetScope(0, m_cdbFrames + index, NULL, 0);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = CdbCore::msgComFailed("SetScope", hr);
|
|
||||||
sg->Release();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// refresh with current frame
|
|
||||||
hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, sg, &sg);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = CdbCore::msgComFailed("GetScopeSymbolGroup", hr);
|
|
||||||
sg->Release();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return sg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CdbStackTraceContext::toString() const
|
QList<StackFrame> CdbStackTraceContext::stackFrames() const
|
||||||
{
|
{
|
||||||
QString rc;
|
// Convert from Core data structures
|
||||||
QTextStream str(&rc);
|
QList<StackFrame> rc;
|
||||||
format(str);
|
const int count = frameCount();
|
||||||
|
const QString hexPrefix = QLatin1String("0x");
|
||||||
|
for(int i = 0; i < count; i++) {
|
||||||
|
const CdbCore::StackFrame &coreFrame = stackFrameAt(i);
|
||||||
|
StackFrame frame;
|
||||||
|
frame.level = i;
|
||||||
|
frame.file = coreFrame.fileName;
|
||||||
|
frame.line = coreFrame.line;
|
||||||
|
frame.function =coreFrame.function;
|
||||||
|
frame.from = coreFrame.module;
|
||||||
|
frame.address = hexPrefix + QString::number(coreFrame.address, 16);
|
||||||
|
rc.push_back(frame);
|
||||||
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CdbStackTraceContext::format(QTextStream &str) const
|
|
||||||
{
|
|
||||||
const int count = m_frames.count();
|
|
||||||
const int defaultFieldWidth = str.fieldWidth();
|
|
||||||
const QTextStream::FieldAlignment defaultAlignment = str.fieldAlignment();
|
|
||||||
for (int f = 0; f < count; f++) {
|
|
||||||
const StackFrame &frame = m_frames.at(f);
|
|
||||||
const bool hasFile = !frame.file.isEmpty();
|
|
||||||
// left-pad level
|
|
||||||
str << qSetFieldWidth(6) << left << f;
|
|
||||||
str.setFieldWidth(defaultFieldWidth);
|
|
||||||
str.setFieldAlignment(defaultAlignment);
|
|
||||||
if (hasFile)
|
|
||||||
str << QDir::toNativeSeparators(frame.file) << ':' << frame.line << " (";
|
|
||||||
str << frame.function;
|
|
||||||
if (hasFile)
|
|
||||||
str << ')';
|
|
||||||
str << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread state helper
|
|
||||||
|
|
||||||
static inline QString msgGetThreadStateFailed(unsigned long threadId, const QString &why)
|
|
||||||
{
|
|
||||||
return QString::fromLatin1("Unable to determine the state of thread %1: %2").arg(threadId).arg(why);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine information about thread. This changes the
|
|
||||||
// current thread to thread->id.
|
|
||||||
static inline bool getStoppedThreadState(const CdbCore::ComInterfaces &cif,
|
|
||||||
ThreadData *t,
|
|
||||||
QString *errorMessage)
|
|
||||||
{
|
|
||||||
enum { MaxFrames = 2 };
|
|
||||||
ULONG currentThread;
|
|
||||||
HRESULT hr = cif.debugSystemObjects->GetCurrentThreadId(¤tThread);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("GetCurrentThreadId", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (currentThread != t->id) {
|
|
||||||
hr = cif.debugSystemObjects->SetCurrentThreadId(t->id);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("SetCurrentThreadId", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ULONG frameCount;
|
|
||||||
// Ignore the top frame if it is "ntdll!KiFastSystemCallRet", which is
|
|
||||||
// not interesting for display.
|
|
||||||
DEBUG_STACK_FRAME frames[MaxFrames];
|
|
||||||
hr = cif.debugControl->GetStackTrace(0, 0, 0, frames, MaxFrames, &frameCount);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("GetStackTrace", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Ignore the top frame if it is "ntdll!KiFastSystemCallRet", which is
|
|
||||||
// not interesting for display.
|
|
||||||
WCHAR wszBuf[MAX_PATH];
|
|
||||||
for (int frame = 0; frame < MaxFrames; frame++) {
|
|
||||||
cif.debugSymbols->GetNameByOffsetWide(frames[frame].InstructionOffset, wszBuf, MAX_PATH, 0, 0);
|
|
||||||
t->function = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
|
|
||||||
if (frame != 0 || t->function != QLatin1String(CdbStackTraceContext::winFuncFastSystemCallRet)) {
|
|
||||||
t->address = frames[frame].InstructionOffset;
|
|
||||||
cif.debugSymbols->GetNameByOffsetWide(frames[frame].InstructionOffset, wszBuf, MAX_PATH, 0, 0);
|
|
||||||
t->function = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
|
|
||||||
ULONG ulLine;
|
|
||||||
hr = cif.debugSymbols->GetLineByOffsetWide(frames[frame].InstructionOffset, &ulLine, wszBuf, MAX_PATH, 0, 0);
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
t->line = ulLine;
|
|
||||||
// Just display base name
|
|
||||||
t->file = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
|
|
||||||
if (!t->file.isEmpty()) {
|
|
||||||
const int slashPos = t->file.lastIndexOf(QLatin1Char('\\'));
|
|
||||||
if (slashPos != -1)
|
|
||||||
t->file.remove(0, slashPos + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} // was not "ntdll!KiFastSystemCallRet"
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QString msgGetThreadsFailed(const QString &why)
|
|
||||||
{
|
|
||||||
return QString::fromLatin1("Unable to determine the thread information: %1").arg(why);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CdbStackTraceContext::getThreads(const CdbCore::ComInterfaces &cif,
|
bool CdbStackTraceContext::getThreads(const CdbCore::ComInterfaces &cif,
|
||||||
bool isStopped,
|
|
||||||
QList<ThreadData> *threads,
|
QList<ThreadData> *threads,
|
||||||
ULONG *currentThreadId,
|
ULONG *currentThreadId,
|
||||||
QString *errorMessage)
|
QString *errorMessage)
|
||||||
{
|
{
|
||||||
|
// Convert from Core data structures
|
||||||
threads->clear();
|
threads->clear();
|
||||||
ULONG threadCount;
|
ThreadIdFrameMap threadMap;
|
||||||
*currentThreadId = 0;
|
if (!CdbCore::StackTraceContext::getThreads(cif, &threadMap,
|
||||||
HRESULT hr= cif.debugSystemObjects->GetNumberThreads(&threadCount);
|
currentThreadId, errorMessage))
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetNumberThreads", hr));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
const QChar slash = QLatin1Char('/');
|
||||||
// Get ids and index of current
|
const ThreadIdFrameMap::const_iterator cend = threadMap.constEnd();
|
||||||
if (!threadCount)
|
for (ThreadIdFrameMap::const_iterator it = threadMap.constBegin(); it != cend; ++it) {
|
||||||
return true;
|
ThreadData data(it.key());
|
||||||
hr = cif.debugSystemObjects->GetCurrentThreadId(currentThreadId);
|
const CdbCore::StackFrame &coreFrame = it.value();
|
||||||
if (FAILED(hr)) {
|
data.address = coreFrame.address;
|
||||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetCurrentThreadId", hr));
|
data.function = coreFrame.function;
|
||||||
return false;
|
data.line = coreFrame.line;
|
||||||
}
|
// Basename only for brevity
|
||||||
|
const int slashPos = coreFrame.fileName.lastIndexOf(slash);
|
||||||
QVector<ULONG> threadIds(threadCount);
|
data.file = slashPos == -1 ? coreFrame.fileName : coreFrame.fileName.mid(slashPos + 1);
|
||||||
hr = cif.debugSystemObjects->GetThreadIdsByIndex(0, threadCount, &(*threadIds.begin()), 0);
|
threads->push_back(data);
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetThreadIdsByIndex", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (ULONG i = 0; i < threadCount; i++) {
|
|
||||||
ThreadData threadData(threadIds.at(i));
|
|
||||||
if (isStopped) {
|
|
||||||
if (!getStoppedThreadState(cif, &threadData, errorMessage)) {
|
|
||||||
qWarning("%s\n", qPrintable(*errorMessage));
|
|
||||||
errorMessage->clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
threads->push_back(threadData);
|
|
||||||
}
|
|
||||||
// Restore thread id
|
|
||||||
if (isStopped && threads->back().id != *currentThreadId) {
|
|
||||||
hr = cif.debugSystemObjects->SetCurrentThreadId(*currentThreadId);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("SetCurrentThreadId", hr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#define CDBSTACKTRACECONTEXT_H
|
#define CDBSTACKTRACECONTEXT_H
|
||||||
|
|
||||||
#include "stackhandler.h"
|
#include "stackhandler.h"
|
||||||
|
#include "stacktracecontext.h"
|
||||||
|
|
||||||
#include "cdbcom.h"
|
#include "cdbcom.h"
|
||||||
|
|
||||||
@@ -54,65 +55,38 @@ class CdbStackFrameContext;
|
|||||||
class CdbDumperHelper;
|
class CdbDumperHelper;
|
||||||
struct ThreadData;
|
struct ThreadData;
|
||||||
|
|
||||||
/* Context representing a break point stack consisting of several frames.
|
/* CdbStackTraceContext: Bridges CdbCore data structures and
|
||||||
* Maintains an on-demand constructed list of CdbStackFrameContext
|
* Debugger structures and handles CdbSymbolGroupContext's. */
|
||||||
* containining the local variables of the stack. */
|
|
||||||
|
|
||||||
class CdbStackTraceContext
|
class CdbStackTraceContext : public CdbCore::StackTraceContext
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY(CdbStackTraceContext)
|
Q_DISABLE_COPY(CdbStackTraceContext)
|
||||||
|
|
||||||
explicit CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper);
|
explicit CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum { maxFrames = 100 };
|
|
||||||
|
|
||||||
// Some well known-functions
|
|
||||||
static const char *winFuncFastSystemCallRet;
|
|
||||||
// WaitFor...
|
|
||||||
static const char *winFuncWaitForPrefix;
|
|
||||||
static const char *winFuncMsgWaitForPrefix;
|
|
||||||
|
|
||||||
// Dummy function used for interrupting a debuggee
|
|
||||||
static const char *winFuncDebugBreakPoint;
|
|
||||||
|
|
||||||
~CdbStackTraceContext();
|
|
||||||
static CdbStackTraceContext *create(const QSharedPointer<CdbDumperHelper> &dumper,
|
static CdbStackTraceContext *create(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||||
unsigned long threadid,
|
|
||||||
QString *errorMessage);
|
QString *errorMessage);
|
||||||
|
|
||||||
QList<StackFrame> frames() const { return m_frames; }
|
CdbSymbolGroupContext *cdbSymbolGroupContextAt(int index, QString *errorMessage);
|
||||||
inline int frameCount() const { return m_frames.size(); }
|
|
||||||
// Search for function. Should ideally contain the module as 'module!foo'.
|
|
||||||
int indexOf(const QString &function) const;
|
|
||||||
|
|
||||||
// Top-Level instruction offset for disassembler
|
QList<StackFrame> stackFrames() const;
|
||||||
ULONG64 instructionOffset() const { return m_instructionOffset; }
|
|
||||||
|
|
||||||
CdbStackFrameContext *frameContextAt(int index, QString *errorMessage);
|
// get threads in stopped state
|
||||||
|
|
||||||
// Format for logging
|
|
||||||
void format(QTextStream &str) const;
|
|
||||||
QString toString() const;
|
|
||||||
|
|
||||||
// Retrieve information about threads. When stopped, add
|
|
||||||
// current stack frame.
|
|
||||||
static bool getThreads(const CdbCore::ComInterfaces &cif,
|
static bool getThreads(const CdbCore::ComInterfaces &cif,
|
||||||
bool isStopped,
|
|
||||||
QList<ThreadData> *threads,
|
QList<ThreadData> *threads,
|
||||||
ULONG *currentThreadId,
|
ULONG *currentThreadId,
|
||||||
QString *errorMessage);
|
QString *errorMessage);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual CdbCore::SymbolGroupContext *createSymbolGroup(const CdbCore::ComInterfaces &cif,
|
||||||
|
int index,
|
||||||
|
const QString &prefix,
|
||||||
|
CIDebugSymbolGroup *comSymbolGroup,
|
||||||
|
QString *errorMessage);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool init(unsigned long frameCount, QString *errorMessage);
|
|
||||||
CIDebugSymbolGroup *createSymbolGroup(int index, QString *errorMessage);
|
|
||||||
|
|
||||||
const QSharedPointer<CdbDumperHelper> m_dumper;
|
const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||||
const CdbCore::ComInterfaces *m_cif;
|
|
||||||
|
|
||||||
DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
|
|
||||||
QVector <CdbStackFrameContext*> m_frameContexts;
|
|
||||||
QList<StackFrame> m_frames;
|
|
||||||
ULONG64 m_instructionOffset;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -31,12 +31,13 @@
|
|||||||
#define CDBSYMBOLGROUPCONTEXT_H
|
#define CDBSYMBOLGROUPCONTEXT_H
|
||||||
|
|
||||||
#include "cdbcom.h"
|
#include "cdbcom.h"
|
||||||
#include "watchhandler.h"
|
#include "symbolgroupcontext.h"
|
||||||
|
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtCore/QVector>
|
#include <QtCore/QVector>
|
||||||
#include <QtCore/QList>
|
#include <QtCore/QList>
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
#include <QtCore/QPair>
|
#include <QtCore/QPair>
|
||||||
#include <QtCore/QMap>
|
#include <QtCore/QMap>
|
||||||
#include <QtCore/QSet>
|
#include <QtCore/QSet>
|
||||||
@@ -47,50 +48,47 @@ namespace Internal {
|
|||||||
class WatchData;
|
class WatchData;
|
||||||
class WatchHandler;
|
class WatchHandler;
|
||||||
struct CdbSymbolGroupRecursionContext;
|
struct CdbSymbolGroupRecursionContext;
|
||||||
|
class CdbDumperHelper;
|
||||||
|
|
||||||
/* A thin wrapper around the IDebugSymbolGroup2 interface which represents
|
|
||||||
* a flat list of symbols using an index (for example, belonging to a stack
|
|
||||||
* frame). It uses the hierarchical naming convention of WatchHandler as in:
|
|
||||||
* "local" (invisible root)
|
|
||||||
* "local.string" (local class variable)
|
|
||||||
* "local.string.data" (class member)
|
|
||||||
* and maintains a mapping iname -> index.
|
|
||||||
* IDebugSymbolGroup2 can "expand" expandable symbols, inserting them into the
|
|
||||||
* flat list after their parent.
|
|
||||||
*
|
|
||||||
* Note the pecularity of IDebugSymbolGroup2 with regard to pointed to items:
|
|
||||||
* 1) A pointer to a POD (say int *) will expand to a pointed-to integer named '*'.
|
|
||||||
* 2) A pointer to a class (QString *), will expand to the class members right away,
|
|
||||||
* omitting the '*' derefenced item. That is a problem since the dumpers trigger
|
|
||||||
* only on the derefenced item, so, additional handling is required.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class CdbSymbolGroupContext
|
/* CdbSymbolGroupContext manages a symbol group context and
|
||||||
|
* a dumper context. It dispatches calls between the local items
|
||||||
|
* that are handled by the symbol group and those that are handled by the dumpers. */
|
||||||
|
|
||||||
|
class CdbSymbolGroupContext : public CdbCore::SymbolGroupContext
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY(CdbSymbolGroupContext);
|
Q_DISABLE_COPY(CdbSymbolGroupContext);
|
||||||
explicit CdbSymbolGroupContext(const QString &prefix,
|
explicit CdbSymbolGroupContext(const QString &prefix,
|
||||||
CIDebugSymbolGroup *symbolGroup,
|
CIDebugSymbolGroup *symbolGroup,
|
||||||
const QStringList &uninitializedVariables = QStringList());
|
const QSharedPointer<CdbDumperHelper> &dumper,
|
||||||
|
const QStringList &uninitializedVariables);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~CdbSymbolGroupContext();
|
// Mask bits for the source field of watch data.
|
||||||
static CdbSymbolGroupContext *create(const QString &prefix,
|
enum { SourceMask = 0xFF, ChildrenKnownBit = 0x0100 };
|
||||||
CIDebugSymbolGroup *symbolGroup,
|
|
||||||
const QStringList &uninitializedVariables,
|
|
||||||
QString *errorMessage);
|
|
||||||
|
|
||||||
QString prefix() const { return m_prefix; }
|
static CdbSymbolGroupContext *create(const QString &prefix,
|
||||||
|
CIDebugSymbolGroup *symbolGroup,
|
||||||
|
const QSharedPointer<CdbDumperHelper> &dumper,
|
||||||
|
const QStringList &uninitializedVariables,
|
||||||
|
QString *errorMessage);
|
||||||
|
|
||||||
bool assignValue(const QString &iname, const QString &value,
|
bool editorToolTip(const QString &iname, QString *value, QString *errorMessage);
|
||||||
QString *newValue /* = 0 */, QString *errorMessage);
|
|
||||||
|
|
||||||
|
bool populateModelInitially(WatchHandler *wh, QString *errorMessage);
|
||||||
|
|
||||||
|
bool completeData(const WatchData &incompleteLocal,
|
||||||
|
WatchHandler *wh,
|
||||||
|
QString *errorMessage);
|
||||||
|
|
||||||
|
private:
|
||||||
// Initially populate the locals model for a new stackframe.
|
// Initially populate the locals model for a new stackframe.
|
||||||
// Write a sequence of WatchData to it, recurse down if the
|
// Write a sequence of WatchData to it, recurse down if the
|
||||||
// recursionPredicate agrees. The ignorePredicate can be used
|
// recursionPredicate agrees. The ignorePredicate can be used
|
||||||
// to terminate processing after insertion of an item (if the calling
|
// to terminate processing after insertion of an item (if the calling
|
||||||
// routine wants to insert another subtree).
|
// routine wants to insert another subtree).
|
||||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||||
static bool populateModelInitially(const CdbSymbolGroupRecursionContext &ctx,
|
static bool populateModelInitiallyHelper(const CdbSymbolGroupRecursionContext &ctx,
|
||||||
OutputIterator it,
|
OutputIterator it,
|
||||||
RecursionPredicate recursionPredicate,
|
RecursionPredicate recursionPredicate,
|
||||||
IgnorePredicate ignorePredicate,
|
IgnorePredicate ignorePredicate,
|
||||||
@@ -102,7 +100,7 @@ public:
|
|||||||
// to terminate processing after insertion of an item (if the calling
|
// to terminate processing after insertion of an item (if the calling
|
||||||
// routine wants to insert another subtree).
|
// routine wants to insert another subtree).
|
||||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||||
static bool completeData (const CdbSymbolGroupRecursionContext &ctx,
|
static bool completeDataHelper (const CdbSymbolGroupRecursionContext &ctx,
|
||||||
WatchData incompleteLocal,
|
WatchData incompleteLocal,
|
||||||
OutputIterator it,
|
OutputIterator it,
|
||||||
RecursionPredicate recursionPredicate,
|
RecursionPredicate recursionPredicate,
|
||||||
@@ -116,67 +114,30 @@ public:
|
|||||||
// Retrieve child symbols of prefix as a sequence of WatchData.
|
// Retrieve child symbols of prefix as a sequence of WatchData.
|
||||||
// Is CIDebugDataSpaces is != 0, try internal dumper and set owner
|
// Is CIDebugDataSpaces is != 0, try internal dumper and set owner
|
||||||
template <class OutputIterator>
|
template <class OutputIterator>
|
||||||
bool getDumpChildSymbols(CIDebugDataSpaces *ds, const QString &prefix,
|
bool getDumpChildSymbols(const QString &prefix,
|
||||||
int dumpedOwner,
|
int dumpedOwner,
|
||||||
OutputIterator it, QString *errorMessage);
|
OutputIterator it, QString *errorMessage);
|
||||||
|
|
||||||
WatchData watchDataAt(unsigned long index) const;
|
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||||
// Run the internal dumpers on the symbol
|
static bool insertSymbolRecursion(WatchData wd,
|
||||||
WatchData dumpSymbolAt(CIDebugDataSpaces *ds, unsigned long index);
|
const CdbSymbolGroupRecursionContext &ctx,
|
||||||
|
OutputIterator it,
|
||||||
|
RecursionPredicate recursionPredicate,
|
||||||
|
IgnorePredicate ignorePredicate,
|
||||||
|
QString *errorMessage);
|
||||||
|
|
||||||
bool lookupPrefix(const QString &prefix, unsigned long *index) const;
|
unsigned watchDataAt(unsigned long index, WatchData *);
|
||||||
|
|
||||||
enum SymbolState { LeafSymbol, ExpandedSymbol, CollapsedSymbol };
|
const bool m_useDumpers;
|
||||||
SymbolState symbolState(unsigned long index) const;
|
const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||||
SymbolState symbolState(const QString &prefix) const;
|
|
||||||
|
|
||||||
inline bool isExpanded(unsigned long index) const { return symbolState(index) == ExpandedSymbol; }
|
|
||||||
inline bool isExpanded(const QString &prefix) const { return symbolState(prefix) == ExpandedSymbol; }
|
|
||||||
|
|
||||||
// Dump
|
|
||||||
enum DumperResult { DumperOk, DumperError, DumperNotHandled };
|
|
||||||
DumperResult dump(CIDebugDataSpaces *ds, WatchData *wd);
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef QMap<QString, unsigned long> NameIndexMap;
|
|
||||||
|
|
||||||
static inline bool isSymbolDisplayable(const DEBUG_SYMBOL_PARAMETERS &p);
|
|
||||||
|
|
||||||
bool init(QString *errorMessage);
|
|
||||||
void clear();
|
|
||||||
QString toString(bool verbose = false) const;
|
|
||||||
bool getChildSymbolsPosition(const QString &prefix,
|
|
||||||
unsigned long *startPos,
|
|
||||||
unsigned long *parentId,
|
|
||||||
QString *errorMessage);
|
|
||||||
bool expandSymbol(const QString &prefix, unsigned long index, QString *errorMessage);
|
|
||||||
void populateINameIndexMap(const QString &prefix, unsigned long parentId, unsigned long end);
|
|
||||||
QString symbolINameAt(unsigned long index) const;
|
|
||||||
|
|
||||||
int dumpQString(CIDebugDataSpaces *ds, WatchData *wd);
|
|
||||||
int dumpStdString(WatchData *wd);
|
|
||||||
|
|
||||||
inline DEBUG_SYMBOL_PARAMETERS *symbolParameters() { return &(*m_symbolParameters.begin()); }
|
|
||||||
inline const DEBUG_SYMBOL_PARAMETERS *symbolParameters() const { return &(*m_symbolParameters.constBegin()); }
|
|
||||||
|
|
||||||
const QString m_prefix;
|
|
||||||
const QChar m_nameDelimiter;
|
|
||||||
const QSet<QString> m_uninitializedVariables;
|
|
||||||
|
|
||||||
CIDebugSymbolGroup *m_symbolGroup;
|
|
||||||
NameIndexMap m_inameIndexMap;
|
|
||||||
QVector<DEBUG_SYMBOL_PARAMETERS> m_symbolParameters;
|
|
||||||
int m_unnamedSymbolNumber;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// A convenience struct to save parameters for the model recursion.
|
// A convenience struct to save parameters for the model recursion.
|
||||||
struct CdbSymbolGroupRecursionContext {
|
struct CdbSymbolGroupRecursionContext {
|
||||||
explicit CdbSymbolGroupRecursionContext(CdbSymbolGroupContext *ctx, int internalDumperOwner, CIDebugDataSpaces *ds);
|
explicit CdbSymbolGroupRecursionContext(CdbSymbolGroupContext *ctx, int internalDumperOwner);
|
||||||
|
|
||||||
CdbSymbolGroupContext *context;
|
CdbSymbolGroupContext *context;
|
||||||
int internalDumperOwner;
|
int internalDumperOwner;
|
||||||
CIDebugDataSpaces *dataspaces;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper to a sequence of WatchData into a list.
|
// Helper to a sequence of WatchData into a list.
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
#ifndef CDBSYMBOLGROUPCONTEXT_TPL_H
|
#ifndef CDBSYMBOLGROUPCONTEXT_TPL_H
|
||||||
#define CDBSYMBOLGROUPCONTEXT_TPL_H
|
#define CDBSYMBOLGROUPCONTEXT_TPL_H
|
||||||
|
|
||||||
|
#include "watchhandler.h"
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
@@ -37,20 +39,10 @@ namespace Internal {
|
|||||||
|
|
||||||
enum { debugSgRecursion = 0 };
|
enum { debugSgRecursion = 0 };
|
||||||
|
|
||||||
/* inline static */ bool CdbSymbolGroupContext::isSymbolDisplayable(const DEBUG_SYMBOL_PARAMETERS &p)
|
|
||||||
{
|
|
||||||
if (p.Flags & (DEBUG_SYMBOL_IS_LOCAL|DEBUG_SYMBOL_IS_ARGUMENT))
|
|
||||||
return true;
|
|
||||||
// Do not display static members.
|
|
||||||
if (p.Flags & DEBUG_SYMBOL_READ_ONLY)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class OutputIterator>
|
template <class OutputIterator>
|
||||||
bool CdbSymbolGroupContext::getDumpChildSymbols(CIDebugDataSpaces *ds, const QString &prefix,
|
bool CdbSymbolGroupContext::getDumpChildSymbols(const QString &prefix,
|
||||||
int dumpedOwner,
|
int dumpedOwner,
|
||||||
OutputIterator it, QString *errorMessage)
|
OutputIterator it, QString *errorMessage)
|
||||||
{
|
{
|
||||||
unsigned long start;
|
unsigned long start;
|
||||||
unsigned long parentId;
|
unsigned long parentId;
|
||||||
@@ -58,21 +50,14 @@ bool CdbSymbolGroupContext::getDumpChildSymbols(CIDebugDataSpaces *ds, const QSt
|
|||||||
return false;
|
return false;
|
||||||
// Skip over expanded children. Internal dumping might expand
|
// Skip over expanded children. Internal dumping might expand
|
||||||
// children, so, re-evaluate size in end condition.
|
// children, so, re-evaluate size in end condition.
|
||||||
for (int s = start; s < m_symbolParameters.size(); ++s) {
|
const int count = size();
|
||||||
const DEBUG_SYMBOL_PARAMETERS &p = m_symbolParameters.at(s);
|
for (int s = start; s < count; ++s) {
|
||||||
|
const DEBUG_SYMBOL_PARAMETERS &p = symbolParameterAt(s);
|
||||||
if (p.ParentSymbol == parentId && isSymbolDisplayable(p)) {
|
if (p.ParentSymbol == parentId && isSymbolDisplayable(p)) {
|
||||||
WatchData wd = watchDataAt(s);
|
WatchData wd;
|
||||||
// Run internal dumper, mark ownership
|
const unsigned rc = watchDataAt(s, &wd);
|
||||||
if (ds) {
|
if (rc & InternalDumperMask)
|
||||||
switch (dump(ds, &wd)) {
|
wd.source = dumpedOwner;
|
||||||
case DumperOk:
|
|
||||||
case DumperError: // Not initialized yet, do not run other dumpers
|
|
||||||
wd.source = dumpedOwner;
|
|
||||||
break;
|
|
||||||
case DumperNotHandled:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*it = wd;
|
*it = wd;
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
@@ -86,7 +71,7 @@ bool CdbSymbolGroupContext::getDumpChildSymbols(CIDebugDataSpaces *ds, const QSt
|
|||||||
// children but can expand none, which would lead to invalid parent information
|
// children but can expand none, which would lead to invalid parent information
|
||||||
// (expand icon), though (ignore for simplicity).
|
// (expand icon), though (ignore for simplicity).
|
||||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||||
bool insertSymbolRecursion(WatchData wd,
|
bool CdbSymbolGroupContext::insertSymbolRecursion(WatchData wd,
|
||||||
const CdbSymbolGroupRecursionContext &ctx,
|
const CdbSymbolGroupRecursionContext &ctx,
|
||||||
OutputIterator it,
|
OutputIterator it,
|
||||||
RecursionPredicate recursionPredicate,
|
RecursionPredicate recursionPredicate,
|
||||||
@@ -124,8 +109,7 @@ bool insertSymbolRecursion(WatchData wd,
|
|||||||
return true;
|
return true;
|
||||||
QList<WatchData> watchList;
|
QList<WatchData> watchList;
|
||||||
// This implicitly enforces expansion
|
// This implicitly enforces expansion
|
||||||
if (!ctx.context->getDumpChildSymbols(ctx.dataspaces,
|
if (!ctx.context->getDumpChildSymbols(wd.iname,
|
||||||
wd.iname,
|
|
||||||
ctx.internalDumperOwner,
|
ctx.internalDumperOwner,
|
||||||
WatchDataBackInserter(watchList), errorMessage))
|
WatchDataBackInserter(watchList), errorMessage))
|
||||||
return false;
|
return false;
|
||||||
@@ -145,11 +129,11 @@ bool insertSymbolRecursion(WatchData wd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||||
bool CdbSymbolGroupContext::populateModelInitially(const CdbSymbolGroupRecursionContext &ctx,
|
bool CdbSymbolGroupContext::populateModelInitiallyHelper(const CdbSymbolGroupRecursionContext &ctx,
|
||||||
OutputIterator it,
|
OutputIterator it,
|
||||||
RecursionPredicate recursionPredicate,
|
RecursionPredicate recursionPredicate,
|
||||||
IgnorePredicate ignorePredicate,
|
IgnorePredicate ignorePredicate,
|
||||||
QString *errorMessage)
|
QString *errorMessage)
|
||||||
{
|
{
|
||||||
if (debugSgRecursion)
|
if (debugSgRecursion)
|
||||||
qDebug() << "### CdbSymbolGroupContext::populateModelInitially";
|
qDebug() << "### CdbSymbolGroupContext::populateModelInitially";
|
||||||
@@ -157,7 +141,7 @@ bool CdbSymbolGroupContext::populateModelInitially(const CdbSymbolGroupRecursion
|
|||||||
// Insert root items
|
// Insert root items
|
||||||
QList<WatchData> watchList;
|
QList<WatchData> watchList;
|
||||||
CdbSymbolGroupContext *sg = ctx.context;
|
CdbSymbolGroupContext *sg = ctx.context;
|
||||||
if (!sg->getDumpChildSymbols(ctx.dataspaces, sg->prefix(),
|
if (!sg->getDumpChildSymbols(sg->prefix(),
|
||||||
ctx.internalDumperOwner,
|
ctx.internalDumperOwner,
|
||||||
WatchDataBackInserter(watchList), errorMessage))
|
WatchDataBackInserter(watchList), errorMessage))
|
||||||
return false;
|
return false;
|
||||||
@@ -169,7 +153,7 @@ bool CdbSymbolGroupContext::populateModelInitially(const CdbSymbolGroupRecursion
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||||
bool CdbSymbolGroupContext::completeData(const CdbSymbolGroupRecursionContext &ctx,
|
bool CdbSymbolGroupContext::completeDataHelper(const CdbSymbolGroupRecursionContext &ctx,
|
||||||
WatchData incompleteLocal,
|
WatchData incompleteLocal,
|
||||||
OutputIterator it,
|
OutputIterator it,
|
||||||
RecursionPredicate recursionPredicate,
|
RecursionPredicate recursionPredicate,
|
||||||
|
|||||||
412
src/plugins/debugger/cdb/stacktracecontext.cpp
Normal file
412
src/plugins/debugger/cdb/stacktracecontext.cpp
Normal 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(¤tThread);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = msgGetThreadStateFailed(id, CdbCore::msgComFailed("GetCurrentThreadId", hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (currentThread != id) {
|
||||||
|
hr = cif.debugSystemObjects->SetCurrentThreadId(id);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = msgGetThreadStateFailed(id, CdbCore::msgComFailed("SetCurrentThreadId", hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ULONG frameCount;
|
||||||
|
// Ignore the top frame if it is "ntdll!KiFastSystemCallRet", which is
|
||||||
|
// not interesting for display.
|
||||||
|
DEBUG_STACK_FRAME frames[MaxFrames];
|
||||||
|
hr = cif.debugControl->GetStackTrace(0, 0, 0, frames, MaxFrames, &frameCount);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = msgGetThreadStateFailed(id, CdbCore::msgComFailed("GetStackTrace", hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Ignore the top frame if it is "ntdll!KiFastSystemCallRet", which is
|
||||||
|
// not interesting for display.
|
||||||
|
*topFrame = frameFromFRAME(cif, frames[0]);
|
||||||
|
if (frameCount > 1
|
||||||
|
&& StackTraceContext::specialFunction(topFrame->module, topFrame->function) == KiFastSystemCallRet)
|
||||||
|
*topFrame = frameFromFRAME(cif, frames[1]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString msgGetThreadsFailed(const QString &why)
|
||||||
|
{
|
||||||
|
return QString::fromLatin1("Unable to determine the thread information: %1").arg(why);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StackTraceContext::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
162
src/plugins/debugger/cdb/stacktracecontext.h
Normal file
162
src/plugins/debugger/cdb/stacktracecontext.h
Normal 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
|
||||||
740
src/plugins/debugger/cdb/symbolgroupcontext.cpp
Normal file
740
src/plugins/debugger/cdb/symbolgroupcontext.cpp
Normal 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
|
||||||
183
src/plugins/debugger/cdb/symbolgroupcontext.h
Normal file
183
src/plugins/debugger/cdb/symbolgroupcontext.h
Normal 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
|
||||||
@@ -324,14 +324,19 @@ QString WatchData::msgNotInScope()
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString &WatchData::shadowedNameFormat()
|
||||||
|
{
|
||||||
|
static const QString format = QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>");
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
QString WatchData::shadowedName(const QString &name, int seen)
|
QString WatchData::shadowedName(const QString &name, int seen)
|
||||||
{
|
{
|
||||||
if (seen <= 0)
|
if (seen <= 0)
|
||||||
return name;
|
return name;
|
||||||
return QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>").arg(name).arg(seen);
|
return shadowedNameFormat().arg(name, seen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// WatchModel
|
// WatchModel
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ public:
|
|||||||
|
|
||||||
static QString msgNotInScope();
|
static QString msgNotInScope();
|
||||||
static QString shadowedName(const QString &name, int seen);
|
static QString shadowedName(const QString &name, int seen);
|
||||||
|
static const QString &shadowedNameFormat();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QByteArray iname; // internal name sth like 'local.baz.public.a'
|
QByteArray iname; // internal name sth like 'local.baz.public.a'
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
#include "cdbdebugoutput.h"
|
#include "cdbdebugoutput.h"
|
||||||
#include "cdbpromptthread.h"
|
#include "cdbpromptthread.h"
|
||||||
#include "debugeventcallback.h"
|
#include "debugeventcallback.h"
|
||||||
|
#include "symbolgroupcontext.h"
|
||||||
|
#include "stacktracecontext.h"
|
||||||
|
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
@@ -149,6 +151,45 @@ void CdbApplication::asyncCommand(int command, const QString &arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CdbApplication::printFrame(const QString &arg)
|
||||||
|
{
|
||||||
|
QString errorMessage;
|
||||||
|
do {
|
||||||
|
if (m_stackTrace.isNull()) {
|
||||||
|
errorMessage = QLatin1String("No trace.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bool ok;
|
||||||
|
const int frame = arg.toInt(&ok);
|
||||||
|
if (!ok || frame < 0 || frame >= m_stackTrace->frameCount()) {
|
||||||
|
errorMessage = QLatin1String("Invalid or out of range.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CdbCore::SymbolGroupContext *ctx = m_stackTrace->symbolGroupContextAt(frame, &errorMessage);
|
||||||
|
if (!ctx)
|
||||||
|
break;
|
||||||
|
printf("%s\n", qPrintable(ctx->toString()));
|
||||||
|
} while (false);
|
||||||
|
if (!errorMessage.isEmpty())
|
||||||
|
printf("%s\n", qPrintable(errorMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CdbApplication::queueBreakPoint(const QString &arg)
|
||||||
|
{
|
||||||
|
// Queue file:line
|
||||||
|
const int cpos = arg.lastIndexOf(QLatin1Char(':'));
|
||||||
|
if (cpos == -1)
|
||||||
|
return false;
|
||||||
|
CdbCore::BreakPoint bp;
|
||||||
|
bp.fileName = arg.left(cpos);
|
||||||
|
bool ok;
|
||||||
|
bp.lineNumber = arg.mid(cpos + 1).toInt(&ok);
|
||||||
|
if (!ok || bp.lineNumber < 1)
|
||||||
|
return false;
|
||||||
|
m_queuedBreakPoints.push_back(bp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void CdbApplication::syncCommand(int command, const QString &arg)
|
void CdbApplication::syncCommand(int command, const QString &arg)
|
||||||
{
|
{
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
@@ -163,6 +204,34 @@ void CdbApplication::syncCommand(int command, const QString &arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Sync_Queue: {
|
||||||
|
const QString targs = arg.trimmed();
|
||||||
|
if (targs.isEmpty()) {
|
||||||
|
std::printf("Queue cleared\n");
|
||||||
|
m_queuedCommands.clear();
|
||||||
|
} else {
|
||||||
|
std::printf("Queueing %s\n", qPrintable(targs));
|
||||||
|
m_queuedCommands.push_back(targs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Sync_QueueBreakPoint: {
|
||||||
|
const QString targs = arg.trimmed();
|
||||||
|
if (targs.isEmpty()) {
|
||||||
|
std::printf("Breakpoint queue cleared\n");
|
||||||
|
m_queuedBreakPoints.clear();
|
||||||
|
} else {
|
||||||
|
if (queueBreakPoint(targs)) {
|
||||||
|
std::printf("Queueing breakpoint %s\n", qPrintable(targs));
|
||||||
|
} else {
|
||||||
|
std::printf("BREAKPOINT SYNTAX ERROR: %s\n", qPrintable(targs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Sync_PrintFrame:
|
||||||
|
printFrame(arg);
|
||||||
|
break;
|
||||||
case Unknown:
|
case Unknown:
|
||||||
if (!m_engine->executeDebuggerCommand(arg, &errorMessage))
|
if (!m_engine->executeDebuggerCommand(arg, &errorMessage))
|
||||||
std::printf("%s\n", qPrintable(errorMessage));
|
std::printf("%s\n", qPrintable(errorMessage));
|
||||||
@@ -196,6 +265,7 @@ void CdbApplication::executionCommand(int command, const QString &arg)
|
|||||||
}
|
}
|
||||||
if (ok) {
|
if (ok) {
|
||||||
m_engine->startWatchTimer();
|
m_engine->startWatchTimer();
|
||||||
|
m_stackTrace.reset();
|
||||||
} else {
|
} else {
|
||||||
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
|
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
|
||||||
}
|
}
|
||||||
@@ -203,11 +273,50 @@ void CdbApplication::executionCommand(int command, const QString &arg)
|
|||||||
|
|
||||||
void CdbApplication::debugEvent()
|
void CdbApplication::debugEvent()
|
||||||
{
|
{
|
||||||
|
QString errorMessage;
|
||||||
std::printf("Debug event\n");
|
std::printf("Debug event\n");
|
||||||
|
|
||||||
|
CdbCore::StackTraceContext::ThreadIdFrameMap threads;
|
||||||
|
ULONG currentThreadId;
|
||||||
|
if (CdbCore::StackTraceContext::getThreads(m_engine->interfaces(), &threads,
|
||||||
|
¤tThreadId, &errorMessage)) {
|
||||||
|
printf("%s\n", qPrintable(CdbCore::StackTraceContext::formatThreads(threads)));
|
||||||
|
} else {
|
||||||
|
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
CdbCore::StackTraceContext *trace =
|
||||||
|
CdbCore::StackTraceContext::create(&m_engine->interfaces(),
|
||||||
|
0xFFFF, &errorMessage);
|
||||||
|
if (trace) {
|
||||||
|
m_stackTrace.reset(trace);
|
||||||
|
printf("%s\n", qPrintable(m_stackTrace->toString()));
|
||||||
|
} else {
|
||||||
|
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CdbApplication::processAttached(void *handle)
|
void CdbApplication::processAttached(void *handle)
|
||||||
{
|
{
|
||||||
std::printf("pe\n");
|
std::printf("### Process attached\n");
|
||||||
m_processHandle = handle;
|
m_processHandle = handle;
|
||||||
|
QString errorMessage;
|
||||||
|
// Commands
|
||||||
|
foreach(const QString &qc, m_queuedCommands) {
|
||||||
|
if (m_engine->executeDebuggerCommand(qc, &errorMessage)) {
|
||||||
|
std::printf("'%s' [ok]\n", qPrintable(qc));
|
||||||
|
} else {
|
||||||
|
std::printf("%s\n", qPrintable(errorMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Breakpoints
|
||||||
|
foreach(const CdbCore::BreakPoint &bp, m_queuedBreakPoints) {
|
||||||
|
if (bp.add(m_engine->interfaces().debugControl, &errorMessage)) {
|
||||||
|
std::printf("'%s' [ok]\n", qPrintable(bp.expression()));
|
||||||
|
} else {
|
||||||
|
std::fprintf(stderr, "%s: %s\n",
|
||||||
|
qPrintable(bp.expression()),
|
||||||
|
qPrintable(errorMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,11 +30,16 @@
|
|||||||
#ifndef CDBAPPLICATION_H
|
#ifndef CDBAPPLICATION_H
|
||||||
#define CDBAPPLICATION_H
|
#define CDBAPPLICATION_H
|
||||||
|
|
||||||
|
#include "breakpoint.h"
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
#include <QtCore/QSharedPointer>
|
#include <QtCore/QSharedPointer>
|
||||||
|
#include <QtCore/QScopedPointer>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
|
||||||
namespace CdbCore {
|
namespace CdbCore {
|
||||||
class CoreEngine;
|
class CoreEngine;
|
||||||
|
class StackTraceContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CdbPromptThread;
|
class CdbPromptThread;
|
||||||
@@ -61,10 +66,16 @@ private slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool parseOptions();
|
bool parseOptions();
|
||||||
|
void printFrame(const QString &arg);
|
||||||
|
bool queueBreakPoint(const QString &arg);
|
||||||
|
|
||||||
QString m_engineDll;
|
QString m_engineDll;
|
||||||
QSharedPointer<CdbCore::CoreEngine> m_engine;
|
QSharedPointer<CdbCore::CoreEngine> m_engine;
|
||||||
|
QScopedPointer<CdbCore::StackTraceContext> m_stackTrace;
|
||||||
CdbPromptThread *m_promptThread;
|
CdbPromptThread *m_promptThread;
|
||||||
|
QStringList m_queuedCommands;
|
||||||
|
QList<CdbCore::BreakPoint> m_queuedBreakPoints;
|
||||||
|
|
||||||
void *m_processHandle;
|
void *m_processHandle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ static const char help[] =
|
|||||||
"S binary args Start binary\n"
|
"S binary args Start binary\n"
|
||||||
"I Interrupt\n"
|
"I Interrupt\n"
|
||||||
"G Go\n"
|
"G Go\n"
|
||||||
|
"Q cmd Queue command for execution in AttachProcess\n"
|
||||||
|
"Q Clear command queue\n"
|
||||||
|
"B file:line Queue a breakpoint for adding in AttachProcess\n"
|
||||||
|
"B Clear breakpoint queue\n"
|
||||||
|
"F <n> Print stack frame <n>, 0 being top\n"
|
||||||
"\nThe remaining commands are passed to CDB.\n";
|
"\nThe remaining commands are passed to CDB.\n";
|
||||||
|
|
||||||
CdbPromptThread::CdbPromptThread(QObject *parent) :
|
CdbPromptThread::CdbPromptThread(QObject *parent) :
|
||||||
@@ -75,12 +80,18 @@ static Command evaluateCommand(const QString &cmdToken)
|
|||||||
switch(cmdToken.at(0).toAscii()) {
|
switch(cmdToken.at(0).toAscii()) {
|
||||||
case 'I':
|
case 'I':
|
||||||
return Async_Interrupt;
|
return Async_Interrupt;
|
||||||
|
case 'Q':
|
||||||
|
return Sync_Queue;
|
||||||
|
case 'B':
|
||||||
|
return Sync_QueueBreakPoint;
|
||||||
case 'E':
|
case 'E':
|
||||||
return Sync_EvalExpression;
|
return Sync_EvalExpression;
|
||||||
case 'G':
|
case 'G':
|
||||||
return Execution_Go;
|
return Execution_Go;
|
||||||
case 'S':
|
case 'S':
|
||||||
return Execution_StartBinary;
|
return Execution_StartBinary;
|
||||||
|
case 'F':
|
||||||
|
return Sync_PrintFrame;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ enum Command {
|
|||||||
UnknownCommand = 0,
|
UnknownCommand = 0,
|
||||||
Async_Interrupt = AsyncCommand|1,
|
Async_Interrupt = AsyncCommand|1,
|
||||||
Sync_EvalExpression = SyncCommand|1,
|
Sync_EvalExpression = SyncCommand|1,
|
||||||
|
Sync_Queue = SyncCommand|2,
|
||||||
|
Sync_QueueBreakPoint = SyncCommand|3,
|
||||||
|
Sync_PrintFrame = SyncCommand|4,
|
||||||
Execution_Go = ExecutionCommand|1,
|
Execution_Go = ExecutionCommand|1,
|
||||||
Execution_StartBinary = ExecutionCommand|2
|
Execution_StartBinary = ExecutionCommand|2
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user