summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@nokia.com>2010-07-02 14:35:41 +0200
committerFriedemann Kleint <Friedemann.Kleint@nokia.com>2010-07-02 14:35:41 +0200
commitab6f124e57ab442224433e0c40aabcd5752619d9 (patch)
treef742385c52b8554e087cc23b73bb17033dcf19db
parentdd0978796e103427a4ce3c74d25dd6c26707661a (diff)
downloadqt-creator-ab6f124e57ab442224433e0c40aabcd5752619d9.tar.gz
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.
-rw-r--r--src/plugins/debugger/cdb/cdbdebugengine.cpp28
-rw-r--r--src/plugins/debugger/cdb/cdbdebugengine_p.h2
-rw-r--r--src/plugins/debugger/cdb/cdbstacktracecontext.cpp45
-rw-r--r--src/plugins/debugger/cdb/cdbstacktracecontext.h1
-rw-r--r--src/plugins/debugger/cdb/stacktracecontext.cpp92
-rw-r--r--src/plugins/debugger/cdb/stacktracecontext.h37
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp2
-rw-r--r--src/plugins/debugger/threadshandler.cpp76
8 files changed, 197 insertions, 86 deletions
diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp
index 44799a3f16..a33479a10b 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 &currentThreadState)
{
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, &currentThreadId, &errorMessage))
+ const bool stopped = m_engine->state() == InferiorStopped;
+ if (!CdbStackTraceContext::getThreads(interfaces(),
+ stopped,
+ &threads, &currentThreadId,
+ &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 810a4b1d1d..60d81e2ad2 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 &currentThreadState = 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 6fc617f532..9b0816dc46 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 b36a79154c..74d60aca3a 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 7e159c2765..3486a658a4 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 350c31b849..750f40b2d6 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 47dffc80f4..67b478b606 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 26d41aa833..8eedbbf60e 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&nbsp;id:")
+ << tableRowSeparatorC << thread.id << tableRowEndC;
+ if (!thread.targetId.isEmpty())
+ str << tableRowStartC << ThreadsHandler::tr("Target&nbsp;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&nbsp;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();
}