From 75529d87d382f9403d71cd8d9c1117ad4bfc58f4 Mon Sep 17 00:00:00 2001
From: hjk <qtc-committer@nokia.com>
Date: Mon, 5 Oct 2009 12:00:47 +0200
Subject: [PATCH] debugger: rework 'jump to source' logic

---
 src/plugins/debugger/debuggeractions.cpp |   2 +-
 src/plugins/debugger/debuggermanager.cpp |   4 +-
 src/plugins/debugger/gdb/gdbengine.cpp   | 231 ++++++++++++-----------
 src/plugins/debugger/gdb/gdbengine.h     |   3 +-
 src/plugins/debugger/stackhandler.h      |  11 ++
 5 files changed, 137 insertions(+), 114 deletions(-)

diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp
index 892a79aa225..c5f3addd1d6 100644
--- a/src/plugins/debugger/debuggeractions.cpp
+++ b/src/plugins/debugger/debuggeractions.cpp
@@ -84,7 +84,7 @@ void DebuggerSettings::writeSettings(QSettings *settings) const
    
 SavedAction *DebuggerSettings::item(int code) const
 {
-    QTC_ASSERT(m_items.value(code, 0), return 0);
+    QTC_ASSERT(m_items.value(code, 0), qDebug() << "CODE: " << code; return 0);
     return m_items.value(code, 0);
 }
 
diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp
index b316bd5f5e4..8394cbbf26f 100644
--- a/src/plugins/debugger/debuggermanager.cpp
+++ b/src/plugins/debugger/debuggermanager.cpp
@@ -454,6 +454,7 @@ void DebuggerManager::init()
     //QTreeView *tooltipView = qobject_cast<QTreeView *>(d->m_tooltipWindow);
     //tooltipView->setModel(d->m_watchHandler->model(TooltipsWatch));
     qRegisterMetaType<WatchData>("WatchData");
+    qRegisterMetaType<StackCookie>("StackCookie");
 
     d->m_actions.continueAction = new QAction(tr("Continue"), this);
     d->m_actions.continueAction->setIcon(QIcon(":/debugger/images/debugger_continue_small.png"));
@@ -707,7 +708,6 @@ void DebuggerManager::showStatusMessage(const QString &msg, int timeout)
 
 void DebuggerManager::notifyInferiorStopped()
 {
-    resetLocation();
     setState(InferiorStopped);
     showStatusMessage(tr("Stopped."), 5000);
 }
@@ -1349,7 +1349,7 @@ void DebuggerManager::gotoLocation(const Debugger::Internal::StackFrame &frame,
 {
     if (theDebuggerBoolSetting(OperateByInstruction) || !frame.isUsable()) {
         if (setMarker)
-            resetLocation();
+            emit resetLocationRequested();
         d->m_disassemblerViewAgent.setFrame(frame);
     } else {
         // Connected to the plugin.
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 4c2b2428a6c..aba7017a1ce 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -894,18 +894,13 @@ void GdbEngine::updateAll()
 {
     QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
     tryLoadDebuggingHelpers();
-    updateLocals(); 
-    postCommand(_("-stack-list-frames"), WatchUpdate, CB(handleStackListFrames1), false);
+    postCommand(_("-stack-list-frames"), WatchUpdate, CB(handleStackListFrames),
+        QVariant::fromValue<StackCookie>(StackCookie(false, true)));
     manager()->stackHandler()->setCurrentIndex(0);
     if (supportsThreads())
         postCommand(_("-thread-list-ids"), WatchUpdate, CB(handleStackListThreads), 0);
     manager()->reloadRegisters();
-}
-
-void GdbEngine::handleStackListFrames1(const GdbResponse &response)
-{
-    handleStackListFrames(response);
-    manager()->gotoLocation(manager()->stackHandler()->currentFrame(), true);
+    updateLocals(); 
 }
 
 void GdbEngine::handleQuerySources(const GdbResponse &response)
@@ -1182,29 +1177,6 @@ void GdbEngine::handleAsyncOutput(const GdbMi &data)
 #endif
 }
 
-void GdbEngine::reloadFullStack()
-{
-    QString cmd = _("-stack-list-frames");
-    postCommand(cmd, WatchUpdate, CB(handleStackListFrames), true);
-}
-
-void GdbEngine::reloadStack()
-{
-    QString cmd = _("-stack-list-frames");
-    int stackDepth = theDebuggerAction(MaximalStackDepth)->value().toInt();
-    if (stackDepth && !m_gdbAdapter->isTrkAdapter())
-        cmd += _(" 0 ") + QString::number(stackDepth);
-    postCommand(cmd, WatchUpdate, CB(handleStackListFrames), false);
-    // FIXME: gdb 6.4 symbianelf likes to be asked twice. The first time it
-    // returns with "^error,msg="Previous frame identical to this frame
-    // (corrupt stack?)". Might be related to the fact that we can't
-    // access the memory belonging to the lower frames. But as we know
-    // this sometimes happens, ask the second time immediately instead
-    // of waiting for the first request to fail.
-    if (m_gdbAdapter->isTrkAdapter())
-        postCommand(cmd, WatchUpdate, CB(handleStackListFrames), false);
-}
-
 void GdbEngine::handleStop1(const GdbResponse &response)
 {
     GdbMi data = response.cookie.value<GdbMi>();
@@ -1307,7 +1279,7 @@ void GdbEngine::handleStop2(const GdbMi &data)
     manager()->stackHandler()->setCurrentIndex(0);
     updateLocals(); // Quick shot
 
-    reloadStack();
+    reloadStack(false);
 
     if (supportsThreads()) {
         int currentId = data.findChild("thread-id").data().toInt();
@@ -2235,13 +2207,52 @@ void GdbEngine::reloadSourceFiles()
 //
 //////////////////////////////////////////////////////////////////////
 
+void GdbEngine::selectThread(int index)
+{
+    ThreadsHandler *threadsHandler = manager()->threadsHandler();
+    threadsHandler->setCurrentThread(index);
+
+    QList<ThreadData> threads = threadsHandler->threads();
+    QTC_ASSERT(index < threads.size(), return);
+    int id = threads.at(index).id;
+    showStatusMessage(tr("Retrieving data for stack view..."), 10000);
+    postCommand(_("-thread-select %1").arg(id), CB(handleStackSelectThread));
+}
+
 void GdbEngine::handleStackSelectThread(const GdbResponse &)
 {
+    QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
     //qDebug("FIXME: StackHandler::handleOutput: SelectThread");
     showStatusMessage(tr("Retrieving data for stack view..."), 3000);
-    reloadStack();
+    manager()->reloadRegisters();
+    reloadStack(true);
+    updateLocals(); 
 }
 
+void GdbEngine::reloadFullStack()
+{
+    QString cmd = _("-stack-list-frames");
+    postCommand(cmd, WatchUpdate, CB(handleStackListFrames),
+        QVariant::fromValue<StackCookie>(StackCookie(true, true)));
+}
+
+void GdbEngine::reloadStack(bool forceGotoLocation)
+{
+    QString cmd = _("-stack-list-frames");
+    int stackDepth = theDebuggerAction(MaximalStackDepth)->value().toInt();
+    if (stackDepth && !m_gdbAdapter->isTrkAdapter())
+        cmd += _(" 0 ") + QString::number(stackDepth);
+    // FIXME: gdb 6.4 symbianelf likes to be asked twice. The first time it
+    // returns with "^error,msg="Previous frame identical to this frame
+    // (corrupt stack?)". Might be related to the fact that we can't
+    // access the memory belonging to the lower frames. But as we know
+    // this sometimes happens, ask the second time immediately instead
+    // of waiting for the first request to fail.
+    if (m_gdbAdapter->isTrkAdapter())
+        postCommand(cmd, WatchUpdate);
+    postCommand(cmd, WatchUpdate, CB(handleStackListFrames),
+        QVariant::fromValue<StackCookie>(StackCookie(false, forceGotoLocation)));
+}
 
 StackFrame GdbEngine::parseStackFrame(const GdbMi &frameMi, int level)
 {
@@ -2266,94 +2277,94 @@ void GdbEngine::handleStackListFrames(const GdbResponse &response)
     #else
     bool handleIt = response.resultClass == GdbResultDone;
     #endif
-    if (handleIt) {
-        bool isFull = response.cookie.toBool();
-        QList<StackFrame> stackFrames;
-
-        GdbMi stack = response.data.findChild("stack");
-        if (!stack.isValid()) {
-            qDebug() << "FIXME: stack:" << stack.toString();
-            return;
-        }
+    if (!handleIt) {
+        // That always happens on symbian gdb with
+        // ^error,data={msg="Previous frame identical to this frame (corrupt stack?)"
+        // logstreamoutput="Previous frame identical to this frame (corrupt stack?)\n"
+        //qDebug() << "LISTING STACK FAILED: " << response.toString();
+        return;
+    }
 
-        int topFrame = -1;
+    StackCookie cookie = response.cookie.value<StackCookie>();
+    QList<StackFrame> stackFrames;
 
-        int n = stack.childCount();
-        for (int i = 0; i != n; ++i) {
-            stackFrames.append(parseStackFrame(stack.childAt(i), i));
-            const StackFrame &frame = stackFrames.back();
+    GdbMi stack = response.data.findChild("stack");
+    if (!stack.isValid()) {
+        qDebug() << "FIXME: stack:" << stack.toString();
+        return;
+    }
 
-            #if defined(Q_OS_WIN)
-            const bool isBogus =
-                // Assume this is wrong and points to some strange stl_algobase
-                // implementation. Happens on Karsten's XP system with Gdb 5.50
-                (frame.file.endsWith(__("/bits/stl_algobase.h")) && frame.line == 150)
-                // Also wrong. Happens on Vista with Gdb 5.50
-                   || (frame.function == __("operator new") && frame.line == 151);
+    int targetFrame = -1;
 
-            // Immediately leave bogus frames.
-            if (topFrame == -1 && isBogus) {
-                postCommand(_("-exec-finish"));
-                return;
-            }
-            #endif
+    int n = stack.childCount();
+    for (int i = 0; i != n; ++i) {
+        stackFrames.append(parseStackFrame(stack.childAt(i), i));
+        const StackFrame &frame = stackFrames.back();
 
-            // Initialize top frame to the first valid frame.
-            // FIXME: Check for QFile(frame.fullname).isReadable()?
-            const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty();
-            if (isValid && topFrame == -1)
-                topFrame = i;
+        #if defined(Q_OS_WIN)
+        const bool isBogus =
+            // Assume this is wrong and points to some strange stl_algobase
+            // implementation. Happens on Karsten's XP system with Gdb 5.50
+            (frame.file.endsWith(__("/bits/stl_algobase.h")) && frame.line == 150)
+            // Also wrong. Happens on Vista with Gdb 5.50
+               || (frame.function == __("operator new") && frame.line == 151);
+
+        // Immediately leave bogus frames.
+        if (targetFrame == -1 && isBogus) {
+            postCommand(_("-exec-finish"));
+            return;
         }
-
-        bool canExpand = !isFull 
-            && (n >= theDebuggerAction(MaximalStackDepth)->value().toInt());
-        theDebuggerAction(ExpandStack)->setEnabled(canExpand);
-        manager()->stackHandler()->setFrames(stackFrames, canExpand);
-
-        #ifdef Q_OS_MAC
-        // Mac gdb does not add the location to the "stopped" message,
-        // so the early gotoLocation() was not triggered. Force it here.
-        bool jump = topFrame != -1
-            && !theDebuggerBoolSetting(OperateByInstruction);
-        #else
-        // For topFrame == -1 there is no frame at all, for topFrame == 0
-        // we already issued a 'gotoLocation' when reading the *stopped
-        // message. Also, when OperateByInstruction we always want to
-        // use frame #0.
-        bool jump = topFrame != -1 && topFrame != 0
-            && !theDebuggerBoolSetting(OperateByInstruction);
         #endif
-        
-        if (jump) {
-            const StackFrame &frame = manager()->stackHandler()->currentFrame();
-            qDebug() << "GOTO, 2nd try" << frame.toString() << topFrame;
-            gotoLocation(frame, true);
-        }
-    } else {
-        // That always happens on symbian gdb with
-        // ^error,data={msg="Previous frame identical to this frame (corrupt stack?)"
-        // logstreamoutput="Previous frame identical to this frame (corrupt stack?)\n"
-        //qDebug() << "LISTING STACK FAILED: " << response.toString();
+
+        // Initialize top frame to the first valid frame.
+        // FIXME: Check for QFile(frame.fullname).isReadable()?
+        const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty();
+        if (isValid && targetFrame == -1)
+            targetFrame = i;
     }
-}
 
-void GdbEngine::selectThread(int index)
-{
-    //reset location arrow
-    m_manager->resetLocation();
+    bool canExpand = !cookie.isFull 
+        && (n >= theDebuggerAction(MaximalStackDepth)->value().toInt());
+    theDebuggerAction(ExpandStack)->setEnabled(canExpand);
+    manager()->stackHandler()->setFrames(stackFrames, canExpand);
 
-    ThreadsHandler *threadsHandler = manager()->threadsHandler();
-    threadsHandler->setCurrentThread(index);
+    // We can't jump to any file if we don't have any frames.
+    if (stackFrames.isEmpty())
+        return;
 
-    QList<ThreadData> threads = threadsHandler->threads();
-    QTC_ASSERT(index < threads.size(), return);
-    int id = threads.at(index).id;
-    showStatusMessage(tr("Retrieving data for stack view..."), 10000);
-    postCommand(_("-thread-select %1").arg(id), CB(handleStackSelectThread));
+    // targetFrame contains the top most frame for which we have source
+    // information. That's typically the frame we'd like to jump to, with
+    // a few exceptions:
+
+    // Always jump to frame #0 when stepping by instruction.
+    if (theDebuggerBoolSetting(OperateByInstruction))
+        targetFrame = 0;
+
+    // If there is no frame with source, jump to frame #0.
+    if (targetFrame == -1)
+        targetFrame = 0;
+
+    #ifdef Q_OS_MAC
+    // Mac gdb does not add the location to the "stopped" message,
+    // so the early gotoLocation() was not triggered. Force it here.
+    bool jump = true;
+    #else
+    // For targetFrame == 0 we already issued a 'gotoLocation'
+    // when reading the *stopped message.
+    bool jump = targetFrame != 0;
+    #endif
+  
+    manager()->stackHandler()->setCurrentIndex(targetFrame);
+    if (jump || cookie.gotoLocation) {
+        const StackFrame &frame = manager()->stackHandler()->currentFrame();
+        //qDebug() << "GOTO, 2ND ATTEMPT: " << frame.toString() << targetFrame;
+        gotoLocation(frame, true);
+    }
 }
 
 void GdbEngine::activateFrame(int frameIndex)
 {
+    m_manager->resetLocation();
     if (state() != InferiorStopped)
         return;
 
@@ -2394,7 +2405,8 @@ void GdbEngine::handleStackListThreads(const GdbResponse &response)
         thread.id = items.at(index).data().toInt();
         threads.append(thread);
         if (thread.id == id) {
-            //qDebug() << "SETTING INDEX TO:" << index << " ID:" << id << " RECOD:" << response.toString();
+            //qDebug() << "SETTING INDEX TO:" << index << " ID:"
+            // << id << " RECOD:" << response.toString();
             currentIndex = index;
         }
     }
@@ -3086,7 +3098,7 @@ void GdbEngine::sendWatchParameters(const QByteArray &params0)
 
 void GdbEngine::handleVarAssign(const GdbResponse &)
 {
-    // everything might have changed, force re-evaluation
+    // Everything might have changed, force re-evaluation.
     // FIXME: Speed this up by re-using variables and only
     // marking values as 'unknown'
     setTokenBarrier();
@@ -3421,7 +3433,8 @@ void GdbEngine::handleStackListArguments(const GdbResponse &response)
         const GdbMi args = frame.findChild("args");
         m_currentFunctionArgs = args.children();
     } else if (response.resultClass == GdbResultError) {
-        qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen";
+        qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen"
+            << response.toString();
     }
 }
 
diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h
index 58352b4ab34..6b5b5ebcb9e 100644
--- a/src/plugins/debugger/gdb/gdbengine.h
+++ b/src/plugins/debugger/gdb/gdbengine.h
@@ -339,10 +339,9 @@ private:
     // Stack specific stuff
     //
     void handleStackListFrames(const GdbResponse &response);
-    void handleStackListFrames1(const GdbResponse &response);
     void handleStackSelectThread(const GdbResponse &response);
     void handleStackListThreads(const GdbResponse &response);
-    Q_SLOT void reloadStack();
+    Q_SLOT void reloadStack(bool forceGotoLocation);
     Q_SLOT void reloadFullStack();
 
 
diff --git a/src/plugins/debugger/stackhandler.h b/src/plugins/debugger/stackhandler.h
index dd2c0a4997f..8b0d7a165c3 100644
--- a/src/plugins/debugger/stackhandler.h
+++ b/src/plugins/debugger/stackhandler.h
@@ -41,6 +41,14 @@
 namespace Debugger {
 namespace Internal {
 
+struct StackCookie
+{
+    StackCookie() : isFull(true), gotoLocation(false) {}
+    StackCookie(bool full, bool jump) : isFull(full), gotoLocation(jump) {}
+    bool isFull;
+    bool gotoLocation;
+};
+
 ////////////////////////////////////////////////////////////////////////
 //
 // StackModel
@@ -129,4 +137,7 @@ private:
 } // namespace Internal
 } // namespace Debugger
 
+Q_DECLARE_METATYPE(Debugger::Internal::StackCookie)
+
+
 #endif // DEBUGGER_STACKHANDLER_H
-- 
GitLab