6d62641c83
This patch is related to PR python/16699, and is an improvement over the patch posted here: <https://sourceware.org/ml/gdb-patches/2014-03/msg00301.html> Keith noticed that, when using the "complete" command on GDB to complete a Python command, some strange things could happen. In order to understand what can go wrong, I need to explain how the Python completion mechanism works. When the user requests a completion of a Python command by using TAB, GDB will first try to determine the right set of "brkchars" that will be used when doing the completion. This is done by actually calling the "complete" method of the Python class. Then, when we already know the "brkchars" that will be used, we call the "complete" method again, for the same values. If you read the thread mentioned above, you will see that one of the design decisions was to make the "cmdpy_completer_helper" (which is the function the does the actual calling of the "complete" method) cache the first result of the completion, since this result will be used in the second call, to do the actual completion. The problem is that the "complete" command does not process the brkchars, and the current Python completion mechanism (improved by the patch mentioned above) relies on GDB trying to determine the brkchars, and then doing the completion itself. Therefore, when we use the "complete" command instead of doing a TAB-completion on GDB, there is a scenario where we can use the invalid cache of a previous Python command that was completed before. For example: (gdb) A <TAB> (gdb) complete B B value1 B value10 B value2 B value3 B value4 B value5 B value6 B value7 B value8 B value9 (gdb) B <TAB> comp1 comp2 comp4 comp6 comp8 comp10 comp3 comp5 comp7 comp9 Here, we see that "complete B " gave a different result than "B <TAB>". The reason for that is because "A <TAB>" was called before, and its completion results were "value*", so when GDB tried to "complete B " it wrongly answered with the results for A. The problem here is using a wrong cache (A's cache) for completing B. We tried to come up with a solution that would preserve the caching mechanism, but it wasn't really possible. So I decided to completely remove the cache, and doing the method calling twice for every completion. This is not optimal, but I do not think it will impact users noticeably. It is worth mentioning another small issue that I found. The code was doing: wordobj = PyUnicode_Decode (word, sizeof (word), host_charset (), NULL); which is totally wrong, because using "sizeof" here will lead to always the same result. So I changed this to use "strlen". The testcase also catches this problem. Keith kindly expanded the existing testcase to cover the problem described above, and everything is passing. gdb/ChangeLog: 2015-04-08 Sergio Durigan Junior <sergiodj@redhat.com> PR python/16699 * python/py-cmd.c (cmdpy_completer_helper): Adjust function to not use a caching mechanism. Adjust comments and code to reflect that. Replace 'sizeof' by 'strlen' when fetching 'wordobj'. (cmdpy_completer_handle_brkchars): Adjust call to cmdpy_completer_helper. Call Py_XDECREF for 'resultobj'. (cmdpy_completer): Likewise. gdb/testsuite/ChangeLog: 2015-04-08 Keith Seitz <keiths@redhat.com> PR python/16699 * gdb.python/py-completion.exp: New tests for completion. * gdb.python/py-completion.py (CompleteLimit1): New class. (CompleteLimit2): Likewise. (CompleteLimit3): Likewise. (CompleteLimit4): Likewise. (CompleteLimit5): Likewise. (CompleteLimit6): Likewise. (CompleteLimit7): Likewise.
147 lines
4.5 KiB
Python
147 lines
4.5 KiB
Python
# Copyright (C) 2014-2015 Free Software Foundation, Inc.
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
# This testcase tests PR python/16699
|
|
|
|
import gdb
|
|
|
|
class CompleteFileInit(gdb.Command):
|
|
def __init__(self):
|
|
gdb.Command.__init__(self,'completefileinit',gdb.COMMAND_USER,gdb.COMPLETE_FILENAME)
|
|
|
|
def invoke(self,argument,from_tty):
|
|
raise gdb.GdbError('not implemented')
|
|
|
|
class CompleteFileMethod(gdb.Command):
|
|
def __init__(self):
|
|
gdb.Command.__init__(self,'completefilemethod',gdb.COMMAND_USER)
|
|
|
|
def invoke(self,argument,from_tty):
|
|
raise gdb.GdbError('not implemented')
|
|
|
|
def complete(self,text,word):
|
|
return gdb.COMPLETE_FILENAME
|
|
|
|
class CompleteFileCommandCond(gdb.Command):
|
|
def __init__(self):
|
|
gdb.Command.__init__(self,'completefilecommandcond',gdb.COMMAND_USER)
|
|
|
|
def invoke(self,argument,from_tty):
|
|
raise gdb.GdbError('not implemented')
|
|
|
|
def complete(self,text,word):
|
|
# This is a test made to know if the command
|
|
# completion still works fine. When the user asks to
|
|
# complete something like "completefilecommandcond
|
|
# /path/to/py-completion-t", it should not complete to
|
|
# "/path/to/py-completion-test/", but instead just
|
|
# wait for input.
|
|
if "py-completion-t" in text:
|
|
return gdb.COMPLETE_COMMAND
|
|
else:
|
|
return gdb.COMPLETE_FILENAME
|
|
|
|
class CompleteLimit1(gdb.Command):
|
|
def __init__(self):
|
|
gdb.Command.__init__(self,'completelimit1',gdb.COMMAND_USER)
|
|
|
|
def invoke(self,argument,from_tty):
|
|
raise gdb.GdbError('not implemented')
|
|
|
|
def complete(self,text,word):
|
|
return ["cl11", "cl12", "cl13"]
|
|
|
|
class CompleteLimit2(gdb.Command):
|
|
def __init__(self):
|
|
gdb.Command.__init__(self,'completelimit2',
|
|
gdb.COMMAND_USER)
|
|
|
|
def invoke(self,argument,from_tty):
|
|
raise gdb.GdbError('not implemented')
|
|
|
|
def complete(self,text,word):
|
|
return ["cl21", "cl23", "cl25", "cl27", "cl29",
|
|
"cl22", "cl24", "cl26", "cl28", "cl210"]
|
|
|
|
class CompleteLimit3(gdb.Command):
|
|
def __init__(self):
|
|
gdb.Command.__init__(self,'completelimit3',
|
|
gdb.COMMAND_USER)
|
|
|
|
def invoke(self,argument,from_tty):
|
|
raise gdb.GdbError('not implemented')
|
|
|
|
def complete(self,text,word):
|
|
return ["cl31", "cl33", "cl35", "cl37", "cl39",
|
|
"cl32", "cl34", "cl36", "cl38", "cl310"]
|
|
|
|
class CompleteLimit4(gdb.Command):
|
|
def __init__(self):
|
|
gdb.Command.__init__(self,'completelimit4',
|
|
gdb.COMMAND_USER)
|
|
|
|
def invoke(self,argument,from_tty):
|
|
raise gdb.GdbError('not implemented')
|
|
|
|
def complete(self,text,word):
|
|
return ["cl41", "cl43", "cl45", "cl47", "cl49",
|
|
"cl42", "cl44", "cl46", "cl48", "cl410"]
|
|
|
|
class CompleteLimit5(gdb.Command):
|
|
def __init__(self):
|
|
gdb.Command.__init__(self,'completelimit5',
|
|
gdb.COMMAND_USER)
|
|
|
|
def invoke(self,argument,from_tty):
|
|
raise gdb.GdbError('not implemented')
|
|
|
|
def complete(self,text,word):
|
|
return ["cl51", "cl53", "cl55", "cl57", "cl59",
|
|
"cl52", "cl54", "cl56", "cl58", "cl510"]
|
|
|
|
class CompleteLimit6(gdb.Command):
|
|
def __init__(self):
|
|
gdb.Command.__init__(self,'completelimit6',
|
|
gdb.COMMAND_USER)
|
|
|
|
def invoke(self,argument,from_tty):
|
|
raise gdb.GdbError('not implemented')
|
|
|
|
def complete(self,text,word):
|
|
return ["cl61", "cl63", "cl65", "cl67", "cl69",
|
|
"cl62", "cl64", "cl66", "cl68", "cl610"]
|
|
|
|
class CompleteLimit7(gdb.Command):
|
|
def __init__(self):
|
|
gdb.Command.__init__(self,'completelimit7',
|
|
gdb.COMMAND_USER)
|
|
|
|
def invoke(self,argument,from_tty):
|
|
raise gdb.GdbError('not implemented')
|
|
|
|
def complete(self,text,word):
|
|
return ["cl71", "cl73", "cl75", "cl77", "cl79",
|
|
"cl72", "cl74", "cl76", "cl78", "cl710"]
|
|
|
|
CompleteFileInit()
|
|
CompleteFileMethod()
|
|
CompleteFileCommandCond()
|
|
CompleteLimit1()
|
|
CompleteLimit2()
|
|
CompleteLimit3()
|
|
CompleteLimit4()
|
|
CompleteLimit5()
|
|
CompleteLimit6()
|
|
CompleteLimit7()
|