diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index 44799a3f163652b487f8125a6d2a47c9b7e17068..a33479a10ba4aa0a169d7ebd2d8b4fe209f86155 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -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; } diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h index 810a4b1d1dbf56575c5e6664697d1f8ef7a6233c..60d81e2ad2c3a763783e39b82a05c27889a12f3a 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine_p.h +++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h @@ -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(); diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp index 6fc617f532969e1b475158abe01251010a13bbbd..9b0816dc4607514dee3ecd3b908072ba8b8d570d 100644 --- a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp @@ -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 diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.h b/src/plugins/debugger/cdb/cdbstacktracecontext.h index b36a79154c2f98e22e7404206f386f41193c2091..74d60aca3adcfba679b1079684560403a2a1cee6 100644 --- a/src/plugins/debugger/cdb/cdbstacktracecontext.h +++ b/src/plugins/debugger/cdb/cdbstacktracecontext.h @@ -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); diff --git a/src/plugins/debugger/cdb/stacktracecontext.cpp b/src/plugins/debugger/cdb/stacktracecontext.cpp index 7e159c2765bd9b611d99f3ff7b60674f8599c6cf..3486a658a4987a145639b53333661e9a2ca19223 100644 --- a/src/plugins/debugger/cdb/stacktracecontext.cpp +++ b/src/plugins/debugger/cdb/stacktracecontext.cpp @@ -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(); diff --git a/src/plugins/debugger/cdb/stacktracecontext.h b/src/plugins/debugger/cdb/stacktracecontext.h index 350c31b8496bab59c941ee794b9ca6880d13bc0d..750f40b2d689721f7803ee9669a0b8933272ff79 100644 --- a/src/plugins/debugger/cdb/stacktracecontext.h +++ b/src/plugins/debugger/cdb/stacktracecontext.h @@ -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, diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 47dffc80f4617aa58180061189b72ef107990a9c..67b478b60654f3899b83e280e278e70f4ec034ca 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -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); diff --git a/src/plugins/debugger/threadshandler.cpp b/src/plugins/debugger/threadshandler.cpp index 26d41aa8339dfae8cc618c2230d9f55926956475..8eedbbf60e08771f1841b58c2c6e18893715fa0e 100644 --- a/src/plugins/debugger/threadshandler.cpp +++ b/src/plugins/debugger/threadshandler.cpp @@ -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(); }