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.
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
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);
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.
Looks like quoting in expressions doesn't work in both patched and original code. I will try to find a solution.
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);
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.
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.
fixed in 4.0 branch: http://gitorious.org/kdevelop/kdevelop/commit/6ca3fd535286c2d562b1efe90edab6e0553df5df
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();