forked from qt-creator/qt-creator
Debugger: Make threads tooltip more verbose
CDB: Add more information and state to Thread. Refactor thread information in CdbCore classes to prepare for more information.
This commit is contained in:
@@ -194,6 +194,7 @@ void CdbDebugEnginePrivate::clearForRun()
|
||||
cleanStackTrace();
|
||||
m_stoppedReason = StoppedOther;
|
||||
m_stoppedMessage.clear();
|
||||
m_engine->threadsHandler()->notifyRunning();
|
||||
}
|
||||
|
||||
void CdbDebugEnginePrivate::cleanStackTrace()
|
||||
@@ -1331,7 +1332,13 @@ void CdbDebugEnginePrivate::handleDebugEvent()
|
||||
if (m_engine->state() != InferiorStopping)
|
||||
m_engine->setState(InferiorStopping, Q_FUNC_INFO, __LINE__);
|
||||
m_engine->setState(InferiorStopped, Q_FUNC_INFO, __LINE__);
|
||||
m_eventThreadId = updateThreadList();
|
||||
// Indicate artifical thread that is created when interrupting as such,
|
||||
// else use stop message with cleaned newlines and blanks.
|
||||
const QString currentThreadState =
|
||||
m_interrupted ? CdbDebugEngine::tr("<interrupt thread>") :
|
||||
(m_stoppedReason == StoppedBreakpoint ? CdbDebugEngine::tr("Breakpoint") :
|
||||
m_stoppedMessage.simplified() );
|
||||
m_eventThreadId = updateThreadList(currentThreadState);
|
||||
m_interruptArticifialThreadId = m_interrupted ? m_eventThreadId : -1;
|
||||
// Get thread to stop and its index. If avoidable, do not use
|
||||
// the artifical thread that is created when interrupting,
|
||||
@@ -1406,7 +1413,7 @@ bool CdbDebugEnginePrivate::setCDBThreadId(unsigned long threadId, QString *erro
|
||||
return true;
|
||||
}
|
||||
|
||||
ULONG CdbDebugEnginePrivate::updateThreadList()
|
||||
ULONG CdbDebugEnginePrivate::updateThreadList(const QString ¤tThreadState)
|
||||
{
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << m_hDebuggeeProcess;
|
||||
@@ -1415,8 +1422,23 @@ ULONG CdbDebugEnginePrivate::updateThreadList()
|
||||
ULONG currentThreadId;
|
||||
QString errorMessage;
|
||||
// When interrupting, an artifical thread with a breakpoint is created.
|
||||
if (!CdbStackTraceContext::getThreads(interfaces(), &threads, ¤tThreadId, &errorMessage))
|
||||
const bool stopped = m_engine->state() == InferiorStopped;
|
||||
if (!CdbStackTraceContext::getThreads(interfaces(),
|
||||
stopped,
|
||||
&threads, ¤tThreadId,
|
||||
&errorMessage))
|
||||
m_engine->warning(errorMessage);
|
||||
// Indicate states 'stopped' or current thread state.
|
||||
// Do not indicate 'running' since we can't know if it is suspended.
|
||||
if (stopped) {
|
||||
const QString state = CdbDebugEngine::tr("stopped");
|
||||
const bool hasCurrentState = !currentThreadState.isEmpty();
|
||||
const int count = threads.size();
|
||||
for (int i= 0; i < count; i++) {
|
||||
threads[i].state = hasCurrentState && threads.at(i).id == currentThreadId ?
|
||||
currentThreadState : state;
|
||||
}
|
||||
}
|
||||
m_engine->threadsHandler()->setThreads(threads);
|
||||
return currentThreadId;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
void setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread);
|
||||
|
||||
bool isDebuggeeRunning() const { return isWatchTimerRunning(); }
|
||||
ULONG updateThreadList();
|
||||
ULONG updateThreadList(const QString ¤tThreadState = QString());
|
||||
bool setCDBThreadId(unsigned long threadId, QString *errorMessage);
|
||||
void updateStackTrace();
|
||||
void updateModules();
|
||||
|
||||
@@ -115,32 +115,45 @@ QList<StackFrame> CdbStackTraceContext::stackFrames() const
|
||||
}
|
||||
|
||||
bool CdbStackTraceContext::getThreads(const CdbCore::ComInterfaces &cif,
|
||||
bool stopped,
|
||||
Threads *threads,
|
||||
ULONG *currentThreadId,
|
||||
QString *errorMessage)
|
||||
{
|
||||
|
||||
QVector<CdbCore::Thread> coreThreads;
|
||||
if (!CdbCore::StackTraceContext::getThreadList(cif, &coreThreads, currentThreadId, errorMessage))
|
||||
return false;
|
||||
// Get frames only if stopped.
|
||||
QVector<CdbCore::StackFrame> frames;
|
||||
if (stopped)
|
||||
if (!CdbCore::StackTraceContext::getStoppedThreadFrames(cif, *currentThreadId,
|
||||
coreThreads, &frames, errorMessage))
|
||||
return false;
|
||||
// Convert from Core data structures
|
||||
threads->clear();
|
||||
ThreadIdFrameMap threadMap;
|
||||
if (!CdbCore::StackTraceContext::getThreads(cif, &threadMap,
|
||||
currentThreadId, errorMessage))
|
||||
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.lineNumber = coreFrame.line;
|
||||
// Basename only for brevity
|
||||
const int slashPos = coreFrame.fileName.lastIndexOf(slash);
|
||||
data.fileName = slashPos == -1 ? coreFrame.fileName : coreFrame.fileName.mid(slashPos + 1);
|
||||
const int count = coreThreads.size();
|
||||
if (!count)
|
||||
return true;
|
||||
threads->reserve(count);
|
||||
const QChar slash(QLatin1Char('/'));
|
||||
for (int i = 0; i < count; i++) {
|
||||
const CdbCore::Thread &coreThread = coreThreads.at(i);
|
||||
ThreadData data(coreThread.id);
|
||||
data.targetId = QLatin1String("0x") + QString::number(coreThread.systemId);
|
||||
if (stopped) {
|
||||
const CdbCore::StackFrame &coreFrame = frames.at(i);
|
||||
data.address = coreFrame.address;
|
||||
data.function = coreFrame.function;
|
||||
data.lineNumber = coreFrame.line;
|
||||
// Basename only for brevity
|
||||
const int slashPos = coreFrame.fileName.lastIndexOf(slash);
|
||||
data.fileName = slashPos == -1 ? coreFrame.fileName : coreFrame.fileName.mid(slashPos + 1);
|
||||
}
|
||||
threads->push_back(data);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
@@ -75,6 +75,7 @@ public:
|
||||
|
||||
// get threads in stopped state
|
||||
static bool getThreads(const CdbCore::ComInterfaces &cif,
|
||||
bool stopped,
|
||||
Threads *threads,
|
||||
ULONG *currentThreadId,
|
||||
QString *errorMessage);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QScopedArrayPointer>
|
||||
|
||||
enum { debug = 0 };
|
||||
|
||||
@@ -74,6 +75,27 @@ void StackFrame::format(QTextStream &str) const
|
||||
str.setIntegerBase(10);
|
||||
}
|
||||
|
||||
Thread::Thread(unsigned long i, unsigned long si) :
|
||||
id(i), systemId(si), dataOffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
QString Thread::toString() const
|
||||
{
|
||||
QString rc;
|
||||
QTextStream str(&rc);
|
||||
str << "Thread id " << id << " System id " << systemId << " Data at 0x";
|
||||
str.setIntegerBase(16);
|
||||
str << dataOffset;
|
||||
return rc;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug d, const Thread &t)
|
||||
{
|
||||
d.nospace() << t.toString();
|
||||
return d;
|
||||
}
|
||||
|
||||
// Check for special functions
|
||||
StackTraceContext::SpecialFunction StackTraceContext::specialFunction(const QString &module,
|
||||
const QString &function)
|
||||
@@ -347,20 +369,21 @@ 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)
|
||||
bool StackTraceContext::getThreadList(const CdbCore::ComInterfaces &cif,
|
||||
QVector<Thread> *threads,
|
||||
ULONG *currentThreadId,
|
||||
QString *errorMessage)
|
||||
{
|
||||
threadIds->clear();
|
||||
threads->clear();
|
||||
ULONG threadCount;
|
||||
*currentThreadId = 0;
|
||||
// Get count
|
||||
HRESULT hr= cif.debugSystemObjects->GetNumberThreads(&threadCount);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetNumberThreads", hr));
|
||||
return false;
|
||||
}
|
||||
// Get ids and index of current
|
||||
// Get index of current
|
||||
if (!threadCount)
|
||||
return true;
|
||||
hr = cif.debugSystemObjects->GetCurrentThreadId(currentThreadId);
|
||||
@@ -368,40 +391,50 @@ bool StackTraceContext::getThreadIds(const CdbCore::ComInterfaces &cif,
|
||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetCurrentThreadId", hr));
|
||||
return false;
|
||||
}
|
||||
threadIds->resize(threadCount);
|
||||
hr = cif.debugSystemObjects->GetThreadIdsByIndex(0, threadCount, &(*threadIds->begin()), 0);
|
||||
// Get Identifiers
|
||||
threads->reserve(threadCount);
|
||||
QScopedArrayPointer<ULONG> ids(new ULONG[threadCount]);
|
||||
QScopedArrayPointer<ULONG> systemIds(new ULONG[threadCount]);
|
||||
hr = cif.debugSystemObjects->GetThreadIdsByIndex(0, threadCount, ids.data(), systemIds.data());
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetThreadIdsByIndex", hr));
|
||||
return false;
|
||||
}
|
||||
// Create entries
|
||||
for (ULONG i= 0; i < threadCount ; i++) {
|
||||
threads->push_back(Thread(ids[i], systemIds[i]));
|
||||
if (ids[i] == *currentThreadId) { // More info for current
|
||||
ULONG64 offset;
|
||||
if (SUCCEEDED(cif.debugSystemObjects->GetCurrentThreadDataOffset(&offset)))
|
||||
threads->back().dataOffset = offset;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StackTraceContext::getThreads(const CdbCore::ComInterfaces &cif,
|
||||
ThreadIdFrameMap *threads,
|
||||
ULONG *currentThreadId,
|
||||
QString *errorMessage)
|
||||
bool StackTraceContext::getStoppedThreadFrames(const CdbCore::ComInterfaces &cif,
|
||||
ULONG currentThreadId,
|
||||
const QVector<Thread> &threads,
|
||||
QVector<StackFrame> *frames,
|
||||
QString *errorMessage)
|
||||
{
|
||||
threads->clear();
|
||||
QVector<ULONG> threadIds;
|
||||
if (!getThreadIds(cif, &threadIds, currentThreadId, errorMessage))
|
||||
return false;
|
||||
if (threadIds.isEmpty())
|
||||
frames->clear();
|
||||
if (threads.isEmpty())
|
||||
return true;
|
||||
frames->reserve(threads.size());
|
||||
|
||||
const int threadCount = threadIds.size();
|
||||
const int threadCount = threads.size();
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
const ULONG id = threadIds.at(i);
|
||||
StackFrame frame;
|
||||
if (!getStoppedThreadState(cif, id, &frame, errorMessage)) {
|
||||
if (!getStoppedThreadState(cif, threads.at(i).id, &frame, errorMessage)) {
|
||||
qWarning("%s\n", qPrintable(*errorMessage));
|
||||
errorMessage->clear();
|
||||
}
|
||||
threads->insert(id, frame);
|
||||
frames->append(frame);
|
||||
}
|
||||
// Restore thread id
|
||||
if (threadIds.back() != *currentThreadId) {
|
||||
const HRESULT hr = cif.debugSystemObjects->SetCurrentThreadId(*currentThreadId);
|
||||
if (threads.back().id != currentThreadId) {
|
||||
const HRESULT hr = cif.debugSystemObjects->SetCurrentThreadId(currentThreadId);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("SetCurrentThreadId", hr));
|
||||
return false;
|
||||
@@ -410,19 +443,6 @@ bool StackTraceContext::getThreads(const CdbCore::ComInterfaces &cif,
|
||||
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();
|
||||
|
||||
@@ -63,7 +63,19 @@ struct StackFrame {
|
||||
ULONG64 address;
|
||||
};
|
||||
|
||||
struct Thread {
|
||||
explicit Thread(unsigned long id = 0, unsigned long sysId = 0);
|
||||
QString toString() const;
|
||||
|
||||
unsigned long id;
|
||||
unsigned long systemId;
|
||||
quint64 dataOffset; // Only for current.
|
||||
};
|
||||
|
||||
inline bool operator<(const Thread &t1, const Thread &t2) { return t1.id < t2.id; }
|
||||
|
||||
QDebug operator<<(QDebug d, const StackFrame &);
|
||||
QDebug operator<<(QDebug d, const Thread &);
|
||||
|
||||
/* Context representing a break point stack consisting of several frames.
|
||||
* Maintains an on-demand constructed list of SymbolGroupContext
|
||||
@@ -87,9 +99,6 @@ public:
|
||||
};
|
||||
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();
|
||||
@@ -112,10 +121,10 @@ public:
|
||||
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);
|
||||
static bool getThreadList(const CdbCore::ComInterfaces &cif,
|
||||
QVector<Thread> *threads,
|
||||
ULONG *currentThreadId,
|
||||
QString *errorMessage);
|
||||
|
||||
// Retrieve detailed information about a threads in stopped state.
|
||||
// Potentially changes current thread id.
|
||||
@@ -124,13 +133,13 @@ public:
|
||||
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);
|
||||
// Get the stack traces for threads in stopped state (only, fails when running).
|
||||
// Potentially changes and restores current thread.
|
||||
static bool getStoppedThreadFrames(const CdbCore::ComInterfaces &cif,
|
||||
ULONG currentThreadId,
|
||||
const QVector<Thread> &threads,
|
||||
QVector<StackFrame> *frames,
|
||||
QString *errorMessage);
|
||||
|
||||
protected:
|
||||
virtual SymbolGroupContext *createSymbolGroup(const ComInterfaces &cif,
|
||||
|
||||
@@ -2966,7 +2966,7 @@ void GdbEngine::handleThreadInfo(const GdbResponse &response)
|
||||
const GdbMi frame = item.findChild("frame");
|
||||
ThreadData thread;
|
||||
thread.id = item.findChild("id").data().toInt();
|
||||
thread.targetId = item.findChild("target-id").data().toInt();
|
||||
thread.targetId = QString::fromAscii(item.findChild("target-id").data());
|
||||
thread.core = QString::fromLatin1(item.findChild("core").data());
|
||||
thread.state = QString::fromLatin1(item.findChild("state").data());
|
||||
thread.address = frame.findChild("addr").data().toULongLong(&ok, 0);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "debuggerconstants.h"
|
||||
#include "debuggerengine.h"
|
||||
|
||||
#include <QtCore/QTextStream>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
@@ -58,6 +59,56 @@ void ThreadData::notifyRunning()
|
||||
lineNumber = -1;
|
||||
}
|
||||
|
||||
|
||||
int id;
|
||||
QString targetId;
|
||||
QString core;
|
||||
|
||||
// State information when stopped
|
||||
void notifyRunning(); // Clear state information
|
||||
|
||||
int frameLevel;
|
||||
quint64 address;
|
||||
QString function;
|
||||
QString fileName;
|
||||
QString state;
|
||||
int lineNumber;
|
||||
|
||||
static inline QString threadToolTip(const ThreadData &thread)
|
||||
{
|
||||
const char tableRowStartC[] = "<tr><td>";
|
||||
const char tableRowSeparatorC[] = "</td><td>";
|
||||
const char tableRowEndC[] = "</td>";
|
||||
QString rc;
|
||||
QTextStream str(&rc);
|
||||
str << "<html><head/><body><table>"
|
||||
<< tableRowStartC << ThreadsHandler::tr("Thread id:")
|
||||
<< tableRowSeparatorC << thread.id << tableRowEndC;
|
||||
if (!thread.targetId.isEmpty())
|
||||
str << tableRowStartC << ThreadsHandler::tr("Target id:")
|
||||
<< tableRowSeparatorC << thread.targetId << tableRowEndC;
|
||||
if (!thread.state.isEmpty())
|
||||
str << tableRowStartC << ThreadsHandler::tr("State:")
|
||||
<< tableRowSeparatorC << thread.state << tableRowEndC;
|
||||
if (!thread.core.isEmpty())
|
||||
str << tableRowStartC << ThreadsHandler::tr("Core:")
|
||||
<< tableRowSeparatorC << thread.core << tableRowEndC;
|
||||
|
||||
if (thread.address) {
|
||||
str << tableRowStartC << ThreadsHandler::tr("Stopped at:")
|
||||
<< tableRowSeparatorC;
|
||||
if (!thread.function.isEmpty())
|
||||
str << thread.function << "<br>";
|
||||
if (!thread.fileName.isEmpty())
|
||||
str << thread.fileName << ':' << thread.lineNumber << "<br>";
|
||||
str.setIntegerBase(16);
|
||||
str << "0x" << thread.address;
|
||||
str.setIntegerBase(10);
|
||||
}
|
||||
str << "</table></body></html>";
|
||||
return rc;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// ThreadsHandler
|
||||
@@ -92,7 +143,8 @@ QVariant ThreadsHandler::data(const QModelIndex &index, int role) const
|
||||
return QVariant();
|
||||
const ThreadData &thread = m_threads.at(row);
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
switch (index.column()) {
|
||||
case ThreadData::IdColumn:
|
||||
return thread.id;
|
||||
@@ -112,21 +164,15 @@ QVariant ThreadsHandler::data(const QModelIndex &index, int role) const
|
||||
case ThreadData::StateColumn:
|
||||
return thread.state;
|
||||
}
|
||||
} else if (role == Qt::ToolTipRole) {
|
||||
if (thread.address == 0)
|
||||
return tr("Thread: %1").arg(thread.id);
|
||||
// Stopped
|
||||
if (thread.fileName.isEmpty())
|
||||
return tr("Thread: %1 at %2 (0x%3)").arg(thread.id)
|
||||
.arg(thread.function).arg(thread.address, 0, 16);
|
||||
return tr("Thread: %1 at %2, %3:%4 (0x%5)").
|
||||
arg(thread.id).arg(thread.function, thread.fileName)
|
||||
.arg(thread.lineNumber).arg(thread.address, 0, 16);
|
||||
} else if (role == Qt::DecorationRole && index.column() == 0) {
|
||||
// Return icon that indicates whether this is the active stack frame
|
||||
return (index.row() == m_currentIndex) ? m_positionIcon : m_emptyIcon;
|
||||
case Qt::ToolTipRole:
|
||||
return threadToolTip(thread);
|
||||
case Qt::DecorationRole: // Return icon that indicates whether this is the active stack frame
|
||||
if (index.column() == 0)
|
||||
return (index.row() == m_currentIndex) ? m_positionIcon : m_emptyIcon;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user