Bug 235759 - KDevelop debugger fails to display watch when expression has spaces
Summary: KDevelop debugger fails to display watch when expression has spaces
Status: RESOLVED FIXED
Alias: None
Product: kdevelop
Classification: Applications
Component: CPP Debugger (show other bugs)
Version: unspecified
Platform: Debian testing Linux
: LO minor
Target Milestone: ---
Assignee: kdevelop-bugs-null
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-04-29 22:09 UTC by Valentyn Pavliuchenko
Modified: 2010-05-10 15:53 UTC (History)
1 user (show)

See Also:
Latest Commit:
Version Fixed In:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Valentyn Pavliuchenko 2010-04-29 22:09:46 UTC
Version:           4.0 (using KDE 4.4.2)
Compiler:          4.4.3 20100108 
OS:                Linux
Installed from:    Debian testing/unstable Packages

Sample program:

int main(int argc, char **argv) 
{
    int a = 2;
    int b = 3;
    int c = a + b; //set breakpoint here
    return c;
}

Launch a debugger, stop on a breakpoint on the selected place. 
Add watch "a+b" - it will be ok.
Add watch "a + b" - nothing will be displayed, but it's a correct expression!

KDevelop IMHO should cut spaces if gdb doesn't recognize them.
Comment 1 Valentyn Pavliuchenko 2010-05-02 12:19:37 UTC
Btw, adding quoted "a + b" works ok.

Console output when adding watches:

1. Adding a+b:

kdevelop(7089)/kdevelop (gdb debugger) KDevelop::VariableWidget::slotAddWatch: Trying to add watch
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::queueCmd: QUEUE:  "-var-create --thread 1 --frame 0 var9 @ a+b"
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::debugStateChange: "STATE: +s_dbgBusy "
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::_gdbStateChanged: Debugger state:  16384 :
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::_gdbStateChanged:     ""
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::execute: SEND: "-var-create --thread 1 --frame 0 var9 @ a+b
"
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::processLine: GDB output:  "^done,name="var9",numchild="0",value="5",type="int",thread-id="1",has_more="0""
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::gdbReady: No more commands
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::debugStateChange: "STATE: -s_dbgBusy "
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::_gdbStateChanged: Debugger state:  0 :
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::_gdbStateChanged:     ""
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::processLine: GDB output:  "(gdb) "
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::processLine: No current command

2. Adding a + b:

kdevelop(7089)/kdevelop (gdb debugger) KDevelop::VariableWidget::slotAddWatch: Trying to add watch
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::queueCmd: QUEUE:  "-var-create --thread 1 --frame 0 var10 @ a + b"
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::debugStateChange: "STATE: +s_dbgBusy "
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::_gdbStateChanged: Debugger state:  16384 :
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::_gdbStateChanged:     ""
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::execute: SEND: "-var-create --thread 1 --frame 0 var10 @ a + b
"
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::processLine: GDB output:  "^error,msg="mi_cmd_var_create: Usage: NAME FRAME EXPRESSION.""
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::processLine: Handling error
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::processLine: Invoked custom handler
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::gdbReady: No more commands
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::debugStateChange: "STATE: -s_dbgBusy "
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::_gdbStateChanged: Debugger state:  0 :
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::_gdbStateChanged:     ""
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::processLine: GDB output:  "(gdb) "
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::processLine: No current command


3. Adding "a + b" (with quotes):

kdevelop(7089)/kdevelop (gdb debugger) KDevelop::VariableWidget::slotAddWatch: Trying to add watch
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::queueCmd: QUEUE:  "-var-create --thread 1 --frame 0 var11 @ "a + b""
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::debugStateChange: "STATE: +s_dbgBusy "
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::_gdbStateChanged: Debugger state:  16384 :
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::_gdbStateChanged:     ""
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::execute: SEND: "-var-create --thread 1 --frame 0 var11 @ "a + b"
"
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::processLine: GDB output:  "^done,name="var11",numchild="0",value="5",type="int",thread-id="1",has_more="0""
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::gdbReady: No more commands
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::debugStateChange: "STATE: -s_dbgBusy "
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::_gdbStateChanged: Debugger state:  0 :
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::DebugSession::_gdbStateChanged:     ""
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::processLine: GDB output:  "(gdb) "
kdevelop(7089)/kdevelop (gdb debugger) GDBDebugger::GDB::processLine: No current command
Comment 2 Valentyn Pavliuchenko 2010-05-02 17:20:26 UTC
This patch works for me:

Index: debuggers/gdb/gdbvariable.cpp
===================================================================
--- debuggers/gdb/gdbvariable.cpp       (revision 1120711)
+++ debuggers/gdb/gdbvariable.cpp       (working copy)
@@ -153,7 +153,7 @@
         s->addCommand(
             new GDBCommand(
                 GDBMI::VarCreate,
-                QString("var%1 @ %2").arg(nextId++).arg(expression()),
+                QString("var%1 @ %2").arg(nextId++).arg(enquotedExpression()),
                 new CreateVarobjHandler(this, callback, callbackMethod)));
     }
 }
@@ -315,3 +315,12 @@
 {
     return varobj_;
 }
+
+QString GdbVariable::enquotedExpression() const
+{
+    QString expr = expression();
+    if (expr.indexOf(' ') != -1)
+        expr = expr.prepend('"').append('"');
+    return expr;
+}
+
Index: debuggers/gdb/gdbvariable.h
===================================================================
--- debuggers/gdb/gdbvariable.h (revision 1120711)
+++ debuggers/gdb/gdbvariable.h (working copy)
@@ -59,6 +59,7 @@
         void fetchMoreChildren();
 
     private: // Internal
+        QString enquotedExpression() const;
         friend class ::CreateVarobjHandler;
         friend class ::FetchMoreChildrenHandler;
         void setVarobj(const QString& v);
Comment 3 Niko Sams 2010-05-02 17:30:15 UTC
thank you for the patch. two issues:
- why not add quote always?
- what happens with quotes inside the quotes? Best would be to look up somewhere how the escaping should be done properly.
Comment 4 Valentyn Pavliuchenko 2010-05-02 23:50:32 UTC
Looks like quoting in expressions doesn't work in both patched and original code.

I will try to find a solution.
Comment 5 Valentyn Pavliuchenko 2010-05-03 13:28:01 UTC
Updated patch.

Was tested:
a+b (without spaces)
a + b (with spaces)
"test" (constant string, with quotes)

- all works ok.

Patch itself:

Index: debuggers/gdb/gdbvariable.cpp
===================================================================
--- debuggers/gdb/gdbvariable.cpp       (revision 1120711)
+++ debuggers/gdb/gdbvariable.cpp       (working copy)
@@ -153,7 +153,7 @@
         s->addCommand(
             new GDBCommand(
                 GDBMI::VarCreate,
-                QString("var%1 @ %2").arg(nextId++).arg(expression()),
+                QString("var%1 @ %2").arg(nextId++).arg(enquotedExpression()),
                 new CreateVarobjHandler(this, callback, callbackMethod)));
     }
 }
@@ -315,3 +315,12 @@
 {
     return varobj_;
 }
+
+QString GdbVariable::enquotedExpression() const
+{
+    QString expr = expression();
+    expr.replace('"', "\\\"");
+    expr = expr.prepend('"').append('"');
+    return expr;
+}
+
Index: debuggers/gdb/gdbvariable.h
===================================================================
--- debuggers/gdb/gdbvariable.h (revision 1120711)
+++ debuggers/gdb/gdbvariable.h (working copy)
@@ -59,6 +59,7 @@
         void fetchMoreChildren();
 
     private: // Internal
+        QString enquotedExpression() const;
         friend class ::CreateVarobjHandler;
         friend class ::FetchMoreChildrenHandler;
         void setVarobj(const QString& v);
Comment 6 Valentyn Pavliuchenko 2010-05-03 13:35:05 UTC
Btw, if in any other place expressions are passed to gdb, or any other strings, the following rule must be used:

1. if the string contain spaces, in must be enquoted
2. if the string doesn't contain spaces, it can be passed quotes (but also can be passed without them). So it's easier to use quotes for any strings.
3. if the string contain double quotes, they should be escaped with a backslash.

Please verify that strings are passed correctly everywhere in gdb interaction code.
Comment 7 Niko Sams 2010-05-03 13:53:58 UTC
looks good.

If you have time, could you create a unittest that tests that fix and verifies that it works in future? (see gdb/unittests)

oh, and we should probably switch to the mailinglist or reviewboard for this discussion.
Comment 9 Niko Sams 2010-05-10 15:53:15 UTC
commit 6ca3fd535286c2d562b1efe90edab6e0553df5df
Author: Niko Sams <niko.sams@gmail.com>
Date:   Sat May 8 15:18:22 2010 +0200

    Quote variable expressions correctly.
    
    This fixes expressions containing spaces.
    
    Thanks to Valentyn Pavliuchenko for the patch.
    http://reviewboard.kde.org/r/3935
    BUG: 235759

diff --git a/debuggers/gdb/gdbvariable.cpp b/debuggers/gdb/gdbvariable.cpp
index 6971627..adec741 100644
--- a/debuggers/gdb/gdbvariable.cpp
+++ b/debuggers/gdb/gdbvariable.cpp
@@ -153,7 +153,7 @@ void GdbVariable::attachMaybe(QObject *callback, const char *callbackMethod)
         s->addCommand(
             new GDBCommand(
                 GDBMI::VarCreate,
-                QString("var%1 @ %2").arg(nextId++).arg(expression()),
+                QString("var%1 @ %2").arg(nextId++).arg(enquotedExpression()),
                 new CreateVarobjHandler(this, callback, callbackMethod)));
     }
 }
@@ -315,3 +315,11 @@ const QString& GdbVariable::varobj() const
 {
     return varobj_;
 }
+
+QString GdbVariable::enquotedExpression() const
+{
+    QString expr = expression();
+    expr.replace('"', "\\\"");
+    expr = expr.prepend('"').append('"');
+    return expr;
+}
diff --git a/debuggers/gdb/gdbvariable.h b/debuggers/gdb/gdbvariable.h
index 9ad7e1a..2a44d8b 100644
--- a/debuggers/gdb/gdbvariable.h
+++ b/debuggers/gdb/gdbvariable.h
@@ -61,6 +61,7 @@ namespace KDevelop
     private: // Internal
         friend class ::CreateVarobjHandler;
         friend class ::FetchMoreChildrenHandler;
+        QString enquotedExpression() const;
         void setVarobj(const QString& v);
         QString varobj_;
 
diff --git a/debuggers/gdb/unittests/gdbtest.cpp b/debuggers/gdb/unittests/gdbtest.cpp
index 2dca380..f0b4a25 100644
--- a/debuggers/gdb/unittests/gdbtest.cpp
+++ b/debuggers/gdb/unittests/gdbtest.cpp
@@ -937,6 +937,46 @@ void GdbTest::testVariablesWatches()
     WAIT_FOR_STATE(session, DebugSession::EndedState);
 }
 
+void GdbTest::testVariablesWatchesQuotes()
+{
+    TestDebugSession *session = new TestDebugSession;
+    session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches);
+
+    TestLaunchConfiguration cfg;
+
+    const QString testString("test");
+    const QString quotedTestString("\"" + testString + "\"");
+
+    breakpoints()->addCodeBreakpoint(debugeeFileName, 38);
+    QVERIFY(session->startProgram(&cfg));
+    WAIT_FOR_STATE(session, DebugSession::PausedState);
+
+    variableCollection()->watches()->add(quotedTestString); //just a constant string
+    QTest::qWait(300);
+
+    QModelIndex i = variableCollection()->index(0, 0);
+    QCOMPARE(variableCollection()->rowCount(i), 1);
+    COMPARE_DATA(variableCollection()->index(0, 0, i), quotedTestString);
+    COMPARE_DATA(variableCollection()->index(0, 1, i), "[" + QString::number(testString.length() + 1) + "]");
+
+    QModelIndex testStr = variableCollection()->index(0, 0, i);
+    COMPARE_DATA(variableCollection()->index(0, 0, testStr), "...");
+    variableCollection()->expanded(testStr);
+    QTest::qWait(100);
+    int len = testString.length();
+    for (int ind = 0; ind < len; ind++)
+    {
+        COMPARE_DATA(variableCollection()->index(ind, 0, testStr), QString::number(ind));
+        QChar c = testString.at(ind);
+        QString value = QString::number(c.toLatin1()) + " '" + c + "'";
+        COMPARE_DATA(variableCollection()->index(ind, 1, testStr), value);
+    }
+    COMPARE_DATA(variableCollection()->index(len, 0, testStr), QString::number(len));
+    COMPARE_DATA(variableCollection()->index(len, 1, testStr), "0 '\\000'");
+
+    session->run();
+    WAIT_FOR_STATE(session, DebugSession::EndedState);
+}
 
 void GdbTest::testVariablesWatchesTwoSessions()
 {
diff --git a/debuggers/gdb/unittests/gdbtest.h b/debuggers/gdb/unittests/gdbtest.h
index 5d8caec..e0eccc7 100644
--- a/debuggers/gdb/unittests/gdbtest.h
+++ b/debuggers/gdb/unittests/gdbtest.h
@@ -62,6 +62,7 @@ private Q_SLOTS:
     void testVariablesLocals();
     void testVariablesLocalsStruct();
     void testVariablesWatches();
+    void testVariablesWatchesQuotes();
     void testVariablesWatchesTwoSessions();
     void testVariablesStopDebugger();
     void testVariablesStartSecondSession();