
This commit attempts to improve the help text that is generated for gdb.Parameter objects when the user fails to provide their own documentation. Documentation for a gdb.Parameter is currently pulled from two sources: the class documentation string, and the set_doc/show_doc class attributes. Thus, a fully documented parameter might look like this: class Param_All (gdb.Parameter): """This is the class documentation string.""" show_doc = "Show the state of this parameter" set_doc = "Set the state of this parameter" def get_set_string (self): val = "on" if (self.value == False): val = "off" return "Test Parameter has been set to " + val def __init__ (self, name): super (Param_All, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN) self._value = True Param_All ('param-all') Then in GDB we see this: (gdb) help set param-all Set the state of this parameter This is the class documentation string. Which is fine. But, if the user skips both of the documentation parts like this: class Param_None (gdb.Parameter): def get_set_string (self): val = "on" if (self.value == False): val = "off" return "Test Parameter has been set to " + val def __init__ (self, name): super (Param_None, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN) self._value = True Param_None ('param-none') Now in GDB we see this: (gdb) help set param-none This command is not documented. This command is not documented. That's not great, the duplicated text looks a bit weird. If we drop different parts we get different results. Here's what we get if the user drops the set_doc and show_doc attributes: (gdb) help set param-doc This command is not documented. This is the class documentation string. That kind of sucks, we say it's undocumented, then proceed to print the documentation. Finally, if we drop the class documentation but keep the set_doc and show_doc: (gdb) help set param-set-show Set the state of this parameter This command is not documented. That seems OK. So, I think there's room for improvement. With this patch, for the four cases above we now see this: # All values provided by the user, no change in this case: (gdb) help set param-all Set the state of this parameter This is the class documentation string. # Nothing provided by the user, the first string is now different: (gdb) help set param-none Set the current value of 'param-none'. This command is not documented. # Only the class documentation is provided, the first string is # changed as in the previous case: (gdb) help set param-doc Set the current value of 'param-doc'. This is the class documentation string. # Only the set_doc and show_doc are provided, this case is unchanged # from before the patch: (gdb) help set param-set-show Set the state of this parameter This command is not documented. The one place where this change might be considered a negative is when dealing with prefix commands. If we create a prefix command but don't supply the set_doc / show_doc strings, then this is what we saw before my patch: (gdb) python Param_None ('print param-none') (gdb) help set print set print, set pr, set p Generic command for setting how things print. List of set print subcommands: ... snip ... set print param-none -- This command is not documented. ... snip ... And after my patch: (gdb) python Param_None ('print param-none') (gdb) help set print set print, set pr, set p Generic command for setting how things print. List of set print subcommands: ... snip ... set print param-none -- Set the current value of 'print param-none'. ... snip ... This seems slightly less helpful than before, but I don't think its terrible. Additionally, I've changed what we print when the get_show_string method is not provided in Python. Back when gdb.Parameter was first added to GDB, we didn't provide a show function when registering the internal command object within GDB. As a result, GDB would make use of its "magic" mangling of the show_doc string to create a sentence that would display the current value (see deprecated_show_value_hack in cli/cli-setshow.c). However, when we added support for the get_show_string method to gdb.Parameter, there was an attempt to maintain backward compatibility by displaying the show_doc string with the current value appended, see get_show_value in py-param.c. Unfortunately, this isn't anywhere close to what deprecated_show_value_hack does, and the results are pretty poor, for example, this is GDB before my patch: (gdb) show param-none This command is not documented. off I think we can all agree that this is pretty bad. After my patch, we how show this: (gdb) show param-none The current value of 'param-none' is "off". Which at least is a real sentence, even if it's not very informative. This patch does change the way that the Python API behaves slightly, but only in the cases when the user has missed providing GDB with some information. In most cases I think the new behaviour is a lot better, there's the one case (noted above) which is a bit iffy, but I think is still OK. I've updated the existing gdb.python/py-parameter.exp test to cover the modified behaviour. Finally, I've updated the documentation to (I hope) make it clearer how the various bits of help text come together.
386 lines
15 KiB
Text
386 lines
15 KiB
Text
# Copyright (C) 2010-2022 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 file is part of the GDB testsuite.
|
|
# It tests gdb.parameter and gdb.Parameter.
|
|
|
|
load_lib gdb-python.exp
|
|
|
|
# Start with a fresh gdb.
|
|
clean_restart
|
|
|
|
# Skip all tests if Python scripting is not enabled.
|
|
if { [skip_python_tests] } { continue }
|
|
|
|
proc_with_prefix test_directories { } {
|
|
# We use "." here instead of ":" so that this works on win32 too.
|
|
if { [is_remote host] } {
|
|
# Don't match $srcdir/$subdir because proc gdb_reinitialize_dir
|
|
# doesn't set search directories on remote host.
|
|
set directories ".*\\\$cdir.\\\$cwd"
|
|
} else {
|
|
set escaped_directory [string_to_regexp "$::srcdir/$::subdir"]
|
|
set directories "$escaped_directory.\\\$cdir.\\\$cwd"
|
|
}
|
|
gdb_test "python print (gdb.parameter ('directories'))" $directories
|
|
}
|
|
|
|
proc_with_prefix test_data_directory { } {
|
|
clean_restart
|
|
|
|
# Check we can correctly read the data-directory parameter. First,
|
|
# grab the value as read directly from the GDB CLI.
|
|
set dd ""
|
|
gdb_test_multiple "show data-directory" \
|
|
"find the initial data-directory value" {
|
|
-re -wrap "GDB's data directory is \"(\[^\r\n\]+)\"\\." {
|
|
set dd $expect_out(1,string)
|
|
pass $gdb_test_name
|
|
}
|
|
}
|
|
|
|
# Now print the data-directory from Python.
|
|
gdb_test "python print (gdb.parameter ('data-directory'))" $dd
|
|
|
|
# Next change the data-directory to a relative path. Internally GDB
|
|
# will resolve this to an absolute path, which Python should then see.
|
|
#
|
|
# GDB is currently running in '...../build/gdb/testsuite/' and the
|
|
# test output is being written to:
|
|
# ...../build/gdb/testsuite/outputs/gdb.python/py-parameter/
|
|
#
|
|
# So create the relative path './outputs/gdb.python/py-parameter/' and
|
|
# set the data-directory to that, we should then see the absolute path.
|
|
|
|
set abs_path_to_output_dir [standard_output_file ""]
|
|
set abs_path_to_cwd $::objdir
|
|
set rel_path_to_output_dir \
|
|
[file join "." [string replace ${abs_path_to_output_dir} 0 \
|
|
[string length ${abs_path_to_cwd}] ""]]
|
|
gdb_test_no_output "set data-directory ${rel_path_to_output_dir}"
|
|
|
|
gdb_test "python print (gdb.parameter ('data-directory'))" \
|
|
${abs_path_to_output_dir} \
|
|
"python sees absolute version of data-directory path"
|
|
|
|
# While we're here, check we see the correct path at GDB's CLI.
|
|
gdb_test "show data-directory" \
|
|
"GDB's data directory is \"${abs_path_to_output_dir}\"\\." \
|
|
"check modified data-directory at the CLI"
|
|
|
|
# Now lets set the data-directory back to what it was initially.
|
|
gdb_test_no_output "set data-directory ${dd}" \
|
|
"set data-directory back to its original value"
|
|
|
|
# And check we see the restored value at CLI and from Python.
|
|
gdb_test "show data-directory" \
|
|
"GDB's data directory is \"${dd}\"\\." \
|
|
"check original data-directory was restored at the CLI"
|
|
|
|
gdb_test "python print (gdb.parameter ('data-directory'))" ${dd} \
|
|
"python sees restored data-directory value"
|
|
}
|
|
|
|
# Test a simple boolean parameter.
|
|
proc_with_prefix test_boolean_parameter { } {
|
|
clean_restart
|
|
|
|
gdb_test_multiline "Simple gdb booleanparameter" \
|
|
"python" "" \
|
|
"class TestParam (gdb.Parameter):" "" \
|
|
" \"\"\"When enabled, test param does something useful. When disabled, does nothing.\"\"\"" "" \
|
|
" show_doc = \"Show the state of the boolean test-param\"" ""\
|
|
" set_doc = \"Set the state of the boolean test-param\"" "" \
|
|
" def get_show_string (self, pvalue):" ""\
|
|
" return \"The state of the Test Parameter is \" + pvalue" ""\
|
|
" def get_set_string (self):" ""\
|
|
" val = \"on\"" ""\
|
|
" if (self.value == False):" ""\
|
|
" val = \"off\"" ""\
|
|
" return \"Test Parameter has been set to \" + val" ""\
|
|
" def __init__ (self, name):" "" \
|
|
" super (TestParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN)" "" \
|
|
" self.value = True" "" \
|
|
"test_param = TestParam ('print test-param')" ""\
|
|
"end"
|
|
|
|
gdb_test "python print (test_param.value)" "True" \
|
|
"test boolean parameter value is True"
|
|
gdb_test "show print test-param" \
|
|
"The state of the Test Parameter is on.*" "show parameter on"
|
|
gdb_test "set print test-param off" \
|
|
"Test Parameter has been set to off" "turn off parameter"
|
|
gdb_test "show print test-param" \
|
|
"The state of the Test Parameter is off.*" "show parameter off"
|
|
gdb_test "python print (test_param.value)" "False" \
|
|
"test boolean parameter value is False"
|
|
gdb_test "help show print test-param" \
|
|
[multi_line \
|
|
"Show the state of the boolean test-param" \
|
|
"When enabled, test param does something useful\\. When disabled, does nothing\\."] \
|
|
"test show help"
|
|
gdb_test "help set print test-param" \
|
|
"Set the state of the boolean test-param.*" "test set help"
|
|
gdb_test "help set print" \
|
|
"set print test-param -- Set the state of the boolean test-param.*" \
|
|
"test general help"
|
|
}
|
|
|
|
# Test an enum parameter.
|
|
proc_with_prefix test_enum_parameter { } {
|
|
clean_restart
|
|
|
|
gdb_test_multiline "enum gdb parameter" \
|
|
"python" "" \
|
|
"class TestEnumParam (gdb.Parameter):" "" \
|
|
" \"\"\"When set, test param does something useful. When disabled, does nothing.\"\"\"" "" \
|
|
" show_doc = \"Show the state of the enum\"" ""\
|
|
" set_doc = \"Set the state of the enum\"" "" \
|
|
" def get_show_string (self, pvalue):" ""\
|
|
" return \"The state of the enum is \" + pvalue" ""\
|
|
" def get_set_string (self):" ""\
|
|
" return \"The state of the enum has been set to \" + self.value" ""\
|
|
" def __init__ (self, name):" "" \
|
|
" super (TestEnumParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_ENUM, \[\"one\", \"two\"\])" "" \
|
|
" self.value = \"one\"" "" \
|
|
"test_enum_param = TestEnumParam ('print test-enum-param')" ""\
|
|
"end"
|
|
|
|
gdb_test "python print (test_enum_param.value)" "one" \
|
|
"test enum parameter value is one"
|
|
gdb_test "show print test-enum-param" \
|
|
"The state of the enum is one.*" \
|
|
"show parameter is initial value"
|
|
gdb_test "set print test-enum-param two" \
|
|
"The state of the enum has been set to two" "set enum to two"
|
|
gdb_test "show print test-enum-param" \
|
|
"The state of the enum is two.*" "show parameter is new value"
|
|
gdb_test "python print (test_enum_param.value)" "two" \
|
|
"test enum parameter value is two"
|
|
gdb_test "set print test-enum-param three" \
|
|
"Undefined item: \"three\".*" "set invalid enum parameter"
|
|
}
|
|
|
|
# Test a file parameter.
|
|
proc_with_prefix test_file_parameter { } {
|
|
clean_restart
|
|
|
|
gdb_test_multiline "file gdb parameter" \
|
|
"python" "" \
|
|
"class TestFileParam (gdb.Parameter):" "" \
|
|
" \"\"\"When set, test param does something useful. When disabled, does nothing.\"\"\"" "" \
|
|
" show_doc = \"Show the name of the file\"" ""\
|
|
" set_doc = \"Set the name of the file\"" "" \
|
|
" def get_show_string (self, pvalue):" ""\
|
|
" return \"The name of the file is \" + pvalue" ""\
|
|
" def get_set_string (self):" ""\
|
|
" return \"The name of the file has been changed to \" + self.value" ""\
|
|
" def __init__ (self, name):" "" \
|
|
" super (TestFileParam, self).__init__ (name, gdb.COMMAND_FILES, gdb.PARAM_FILENAME)" "" \
|
|
" self.value = \"foo.txt\"" "" \
|
|
"test_file_param = TestFileParam ('test-file-param')" ""\
|
|
"end"
|
|
|
|
gdb_test "python print (test_file_param.value)" "foo.txt" \
|
|
"test file parameter value"
|
|
gdb_test "show test-file-param" \
|
|
"The name of the file is foo.txt.*" "show initial file value"
|
|
gdb_test "set test-file-param bar.txt" \
|
|
"The name of the file has been changed to bar.txt" \
|
|
"set new file parameter" 1
|
|
gdb_test "show test-file-param" \
|
|
"The name of the file is bar.txt.*" "show new file value"
|
|
gdb_test "python print (test_file_param.value)" \
|
|
"bar.txt" "test new file parameter value"
|
|
gdb_test "set test-file-param" "Argument required.*"
|
|
}
|
|
|
|
# Test a parameter that is not documented.
|
|
proc_with_prefix test_undocumented_parameter { } {
|
|
clean_restart
|
|
|
|
gdb_test_multiline "Simple gdb booleanparameter" \
|
|
"python" "" \
|
|
"class TestUndocParam (gdb.Parameter):" "" \
|
|
" def get_show_string (self, pvalue):" ""\
|
|
" return \"The state of the Test Parameter is \" + pvalue" ""\
|
|
" def get_set_string (self):" ""\
|
|
" val = \"on\"" ""\
|
|
" if (self.value == False):" ""\
|
|
" val = \"off\"" ""\
|
|
" return \"Test Parameter has been set to \" + val" ""\
|
|
" def __init__ (self, name):" "" \
|
|
" super (TestUndocParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN)" "" \
|
|
" self.value = True" "" \
|
|
"test_undoc_param = TestUndocParam ('print test-undoc-param')" ""\
|
|
"end"
|
|
|
|
gdb_test "show print test-undoc-param" \
|
|
"The state of the Test Parameter is on.*" "show parameter on"
|
|
gdb_test "set print test-undoc-param off" \
|
|
"Test Parameter has been set to off" "turn off parameter"
|
|
gdb_test "show print test-undoc-param" \
|
|
"The state of the Test Parameter is off.*" "show parameter off"
|
|
gdb_test "python print (test_undoc_param.value)" \
|
|
"False" "test undocumented parameter value is False"
|
|
gdb_test "help show print test-undoc-param" \
|
|
[multi_line \
|
|
"Show the current value of 'print test-undoc-param'\\." \
|
|
"This command is not documented.*"] \
|
|
"test show help"
|
|
gdb_test "help set print test-undoc-param" \
|
|
"This command is not documented.*" "test set help"
|
|
gdb_test "help set print" \
|
|
"set print test-undoc-param -- Set the current value of 'print test-undoc-param'\\..*" \
|
|
"test general help"
|
|
}
|
|
|
|
# Test a parameter that is not documented in any way..
|
|
proc_with_prefix test_really_undocumented_parameter { } {
|
|
clean_restart
|
|
|
|
gdb_test_multiline "Simple gdb booleanparameter" \
|
|
"python" "" \
|
|
"class TestNodocParam (gdb.Parameter):" "" \
|
|
" def __init__ (self, name):" "" \
|
|
" super (TestNodocParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN)" "" \
|
|
" self.value = True" "" \
|
|
"test_nodoc_param = TestNodocParam ('print test-nodoc-param')" ""\
|
|
"end"
|
|
|
|
gdb_test "show print test-nodoc-param" \
|
|
"The current value of 'print test-nodoc-param' is \"on\"\\." \
|
|
"show parameter on"
|
|
gdb_test_no_output "set print test-nodoc-param off" \
|
|
"turn off parameter"
|
|
gdb_test "show print test-nodoc-param" \
|
|
"The current value of 'print test-nodoc-param' is \"off\"\\." \
|
|
"show parameter off"
|
|
gdb_test "python print (test_nodoc_param.value)" \
|
|
"False" "test really undocumented parameter value is False"
|
|
gdb_test "help show print test-nodoc-param" \
|
|
[multi_line \
|
|
"Show the current value of 'print test-nodoc-param'\\." \
|
|
"This command is not documented.*"] \
|
|
"test show help"
|
|
gdb_test "help set print test-nodoc-param" \
|
|
"This command is not documented.*" "test set help"
|
|
gdb_test "help set print" \
|
|
"set print test-nodoc-param -- Set the current value of 'print test-nodoc-param'\\..*" \
|
|
"test general help"
|
|
}
|
|
|
|
# Test deprecated API. Do not use in your own implementations.
|
|
proc_with_prefix test_deprecated_api_parameter { } {
|
|
clean_restart
|
|
|
|
gdb_test_multiline "Simple gdb booleanparameter" \
|
|
"python" "" \
|
|
"class TestParam (gdb.Parameter):" "" \
|
|
" \"\"\"When enabled, test param does something useful. When disabled, does nothing.\"\"\"" "" \
|
|
" show_doc = \"State of the Test Parameter\"" ""\
|
|
" set_doc = \"Set the state of the Test Parameter\"" "" \
|
|
" def __init__ (self, name):" "" \
|
|
" super (TestParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN)" "" \
|
|
" self.value = True" "" \
|
|
"test_param = TestParam ('print test-param')" ""\
|
|
"end"
|
|
|
|
gdb_test "python print (test_param.value)" "True" \
|
|
"test deprecated API parameter value is True"
|
|
gdb_test "show print test-param" \
|
|
"The current value of 'print test-param' is \"on\"\\." \
|
|
"show parameter on"
|
|
gdb_test_no_output "set print test-param off" "turn off parameter"
|
|
gdb_test "show print test-param" \
|
|
"The current value of 'print test-param' is \"off\"\\." \
|
|
"show parameter off"
|
|
gdb_test "python print (test_param.value)" "False" \
|
|
"test deprecated API parameter value is False"
|
|
gdb_test "help show print test-param" \
|
|
[multi_line \
|
|
"State of the Test Parameter" \
|
|
"When enabled, test param does something useful\\. When disabled, does nothing\\."] \
|
|
"test show help"
|
|
gdb_test "help set print test-param" \
|
|
"Set the state of the Test Parameter.*" "test set help"
|
|
gdb_test "help set print" \
|
|
"set print test-param -- Set the state of the Test Parameter.*" \
|
|
"test general help"
|
|
}
|
|
|
|
proc_with_prefix test_integer_parameter { } {
|
|
foreach_with_prefix kind {PARAM_ZUINTEGER PARAM_ZUINTEGER_UNLIMITED} {
|
|
clean_restart
|
|
|
|
gdb_test_multiline "create parameter" \
|
|
"python" "" \
|
|
"class TestNodocParam (gdb.Parameter):" "" \
|
|
" def __init__ (self, name):" "" \
|
|
" super (TestNodocParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.$kind)" "" \
|
|
" self.value = 0" "" \
|
|
"test_param_$kind = TestNodocParam ('test-$kind')" "" \
|
|
"end"
|
|
|
|
gdb_test "python print(gdb.parameter('test-$kind'))" "0"
|
|
|
|
gdb_test "python test_param_$kind.value = -5" "RuntimeError: Range exceeded.*"
|
|
|
|
if {$kind == "PARAM_ZUINTEGER"} {
|
|
gdb_test "python test_param_$kind.value = -1" "RuntimeError: Range exceeded.*"
|
|
} elseif {$kind == "PARAM_ZUINTEGER_UNLIMITED"} {
|
|
gdb_test_no_output "python test_param_$kind.value = -1" \
|
|
"check that PARAM_ZUINTEGER value can be set to -1"
|
|
gdb_test "python print(gdb.parameter('test-$kind'))" "-1" \
|
|
"check that PARAM_ZUINTEGER value is -1 after setting"
|
|
} else {
|
|
error "invalid kind: $kind"
|
|
}
|
|
}
|
|
}
|
|
|
|
proc_with_prefix test_throwing_parameter { } {
|
|
clean_restart
|
|
|
|
gdb_test_multiline "Throwing gdb parameter" \
|
|
"python" "" \
|
|
"class TestThrowParam (gdb.Parameter):" "" \
|
|
" def __init__ (self, name):" "" \
|
|
" super (TestThrowParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_STRING)" "" \
|
|
" self.value = True" "" \
|
|
" def get_set_string (self):" "" \
|
|
" raise gdb.GdbError('Ordinary gdb error')" "" \
|
|
"test_throw_param = TestThrowParam ('print test-throw-param')" ""\
|
|
"end"
|
|
|
|
gdb_test "set print test-throw-param whoops" \
|
|
"Ordinary gdb error" \
|
|
"gdb.GdbError does not show Python stack"
|
|
}
|
|
|
|
test_directories
|
|
test_data_directory
|
|
test_boolean_parameter
|
|
test_enum_parameter
|
|
test_file_parameter
|
|
test_undocumented_parameter
|
|
test_really_undocumented_parameter
|
|
test_deprecated_api_parameter
|
|
test_integer_parameter
|
|
test_throwing_parameter
|
|
|
|
# This caused a gdb crash.
|
|
gdb_test "python print(gdb.parameter('endian'))" "auto" \
|
|
"print endian parameter"
|