Add a selftest that detects a 'corrupted' command tree structure in GDB.

The GDB data structure that records the GDB commands is made of
'struct cmd_list_element' defined in cli-decode.h.

A cmd_list_element has various pointers to other cmd_list_element structures,
All these pointers are together building a graph of commands.

However, when following the 'next' and '*prefixlist' pointers of
cmd_list_element, the structure must better be a tree.

If such pointers do not form a tree, then some other elements of
cmd_list_element cannot get a correct semantic.  In particular, the prefixname
has no correct meaning if the same prefix command can be reached via 2 different
paths.

This commit introduces a selftest that detects (at least some cases of) errors
leading to 'next' and '*prefixlist' not giving a tree structure.

The new 'command_structure_invariants' selftest detects one single case where
the command structure is not a tree:

  (gdb) maintenance selftest command_structure_invariants
  Running selftest command_structure_invariants.
  list 0x56362e204b98 duplicated, reachable via prefix 'show ' and 'info set '.  Duplicated list first command is 'ada'
  Self test failed: self-test failed at ../../classfix/gdb/unittests/command-def-selftests.c:160
  Ran 1 unit tests, 1 failed
  (gdb)

This was fixed by the previous commit.

2020-05-15  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* unittests/help-doc-selftests.c: Rename to
	unittests/command-def-selftests.c
	* unittests/command-def-selftests.c (help_doc_tests): Update some
	comments.
	(command_structure_tests, traverse_command_structure): New namespace
	and function.
	(command_structure_invariants_tests): New function.
	(_initialize_command_def_selftests) Renamed from
	_initialize_help_doc_selftests, register command_structure_invariants
	selftest.
This commit is contained in:
Philippe Waroquiers 2020-05-04 22:25:24 +02:00
parent a7b9ceb8b4
commit 58e6ac7006
3 changed files with 92 additions and 4 deletions

View File

@ -1,3 +1,16 @@
2020-05-15 Philippe Waroquiers <philippe.waroquiers@skynet.be>
* unittests/help-doc-selftests.c: Rename to
unittests/command-def-selftests.c
* unittests/command-def-selftests.c (help_doc_tests): Update some
comments.
(command_structure_tests, traverse_command_structure): New namespace
and function.
(command_structure_invariants_tests): New function.
(_initialize_command_def_selftests) Renamed from
_initialize_help_doc_selftests, register command_structure_invariants
selftest.
2020-05-15 Philippe Waroquiers <philippe.waroquiers@skynet.be>
* cli/cli-cmds.c (_initialize_cli_cmds): Define 'info set' as

View File

@ -427,13 +427,13 @@ SELFTESTS_SRCS = \
unittests/array-view-selftests.c \
unittests/child-path-selftests.c \
unittests/cli-utils-selftests.c \
unittests/command-def-selftests.c \
unittests/common-utils-selftests.c \
unittests/copy_bitwise-selftests.c \
unittests/environ-selftests.c \
unittests/filtered_iterator-selftests.c \
unittests/format_pieces-selftests.c \
unittests/function-view-selftests.c \
unittests/help-doc-selftests.c \
unittests/lookup_name_info-selftests.c \
unittests/memory-map-selftests.c \
unittests/memrange-selftests.c \

View File

@ -1,4 +1,4 @@
/* Self tests for help doc for GDB, the GNU debugger.
/* Self tests for GDB command definitions for GDB, the GNU debugger.
Copyright (C) 2019-2020 Free Software Foundation, Inc.
@ -22,7 +22,12 @@
#include "cli/cli-decode.h"
#include "gdbsupport/selftest.h"
#include <map>
namespace selftests {
/* Verify some invariants of GDB commands documentation. */
namespace help_doc_tests {
static unsigned int nr_failed_invariants;
@ -96,13 +101,83 @@ help_doc_invariants_tests ()
}
} /* namespace help_doc_tests */
/* Verify some invariants of GDB command structure. */
namespace command_structure_tests {
unsigned int nr_duplicates = 0;
/* A map associating a list with the prefix leading to it. */
std::map<cmd_list_element **, const char *> lists;
/* Store each command list in lists, associated with the prefix to reach it. A
list must only be found once. */
static void
traverse_command_structure (struct cmd_list_element **list,
const char *prefix)
{
struct cmd_list_element *c;
auto dupl = lists.find (list);
if (dupl != lists.end ())
{
fprintf_filtered (gdb_stdout,
"list %p duplicated,"
" reachable via prefix '%s' and '%s'."
" Duplicated list first command is '%s'\n",
list,
prefix, dupl->second,
(*list)->name);
nr_duplicates++;
return;
}
lists.insert ({list, prefix});
/* Walk through the commands. */
for (c = *list; c; c = c->next)
{
/* If this command has subcommands and is not an alias,
traverse the subcommands. */
if (c->prefixlist != NULL && c->cmd_pointer == nullptr)
{
/* Recursively call ourselves on the subcommand list,
passing the right prefix in. */
traverse_command_structure (c->prefixlist, c->prefixname);
}
}
}
/* Verify that a list of commands is present in the tree only once. */
static void
command_structure_invariants_tests ()
{
nr_duplicates = 0;
traverse_command_structure (&cmdlist, "");
/* Release memory, be ready to be re-run. */
lists.clear ();
SELF_CHECK (nr_duplicates == 0);
}
}
} /* namespace selftests */
void _initialize_help_doc_selftests ();
void _initialize_command_def_selftests ();
void
_initialize_help_doc_selftests ()
_initialize_command_def_selftests ()
{
selftests::register_test
("help_doc_invariants",
selftests::help_doc_tests::help_doc_invariants_tests);
selftests::register_test
("command_structure_invariants",
selftests::command_structure_tests::command_structure_invariants_tests);
}