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