Add command to list Ada exceptions

This patch adds a new command "info exceptions" whose purpose is to
provide the list of exceptions currently defined in the inferior.
The usage is:

    (gdb) info exceptions [REGEXP]

Without argument, the command lists all exceptions.  Otherwise,
only those whose name match REGEXP are listed.

For instance:

    (gdb) info exceptions
    All defined Ada exceptions:
    constraint_error: 0x613dc0
    program_error: 0x613d40
    storage_error: 0x613d00
    tasking_error: 0x613cc0
    global_exceptions.a_global_exception: 0x613a80
    global_exceptions.a_private_exception: 0x613ac0

The name of the command, as well as its output is part of a legacy
I inherited long ago. It's output being parsed by frontends such as
GPS, I cannot easily change it. Same for the command name.

The implementation is mostly self-contained, and is written in a way
that should make it easy to implement the GDB/MI equivalent. The
careful reviewer will notice that the code added in ada-lang.h could
normally be made private inside ada-lang.c.  But these will be used
by the GDB/MI implementation.  Rather than making those private now,
only to move them later, I've made them public right away.

gdb/ChangeLog:

        * ada-lang.h: #include "vec.h".
        (struct ada_exc_info): New.
        (ada_exc_info): New typedef.
        (DEF_VEC_O(ada_exc_info)): New vector.
        (ada_exceptions_list): Add declaration.
        * ada-lang.c (ada_is_exception_sym)
        (ada_is_non_standard_exception_sym, compare_ada_exception_info)
        (sort_remove_dups_ada_exceptions_list)
        (ada_exc_search_name_matches, ada_add_standard_exceptions)
        (ada_add_exceptions_from_frame, ada_add_global_exceptions)
        (ada_exceptions_list_1, ada_exceptions_list)
        (info_exceptions_command): New function.
        (_initialize_ada_language): Add "info exception" command.

gdb/testsuite/ChangeLog:

        * gdb.ada/info_exc: New testcase.
This commit is contained in:
Joel Brobecker 2013-11-07 17:40:48 +04:00
parent 304a8ac17c
commit 778865d3e2
7 changed files with 488 additions and 0 deletions

View File

@ -1,3 +1,19 @@
2013-11-12 Joel Brobecker <brobecker@adacore.com>
* ada-lang.h: #include "vec.h".
(struct ada_exc_info): New.
(ada_exc_info): New typedef.
(DEF_VEC_O(ada_exc_info)): New vector.
(ada_exceptions_list): Add declaration.
* ada-lang.c (ada_is_exception_sym)
(ada_is_non_standard_exception_sym, compare_ada_exception_info)
(sort_remove_dups_ada_exceptions_list)
(ada_exc_search_name_matches, ada_add_standard_exceptions)
(ada_add_exceptions_from_frame, ada_add_global_exceptions)
(ada_exceptions_list_1, ada_exceptions_list)
(info_exceptions_command): New function.
(_initialize_ada_language): Add "info exception" command.
2013-11-11 Phil Muldoon <pmuldoon@redhat.com>
PR python/15629

View File

@ -12284,6 +12284,357 @@ catch_assert_command (char *arg, int from_tty,
tempflag, 1 /* enabled */,
from_tty);
}
/* Return non-zero if the symbol SYM is an Ada exception object. */
static int
ada_is_exception_sym (struct symbol *sym)
{
const char *type_name = type_name_no_tag (SYMBOL_TYPE (sym));
return (SYMBOL_CLASS (sym) != LOC_TYPEDEF
&& SYMBOL_CLASS (sym) != LOC_BLOCK
&& SYMBOL_CLASS (sym) != LOC_CONST
&& SYMBOL_CLASS (sym) != LOC_UNRESOLVED
&& type_name != NULL && strcmp (type_name, "exception") == 0);
}
/* Given a global symbol SYM, return non-zero iff SYM is a non-standard
Ada exception object. This matches all exceptions except the ones
defined by the Ada language. */
static int
ada_is_non_standard_exception_sym (struct symbol *sym)
{
int i;
if (!ada_is_exception_sym (sym))
return 0;
for (i = 0; i < ARRAY_SIZE (standard_exc); i++)
if (strcmp (SYMBOL_LINKAGE_NAME (sym), standard_exc[i]) == 0)
return 0; /* A standard exception. */
/* Numeric_Error is also a standard exception, so exclude it.
See the STANDARD_EXC description for more details as to why
this exception is not listed in that array. */
if (strcmp (SYMBOL_LINKAGE_NAME (sym), "numeric_error") == 0)
return 0;
return 1;
}
/* A helper function for qsort, comparing two struct ada_exc_info
objects.
The comparison is determined first by exception name, and then
by exception address. */
static int
compare_ada_exception_info (const void *a, const void *b)
{
const struct ada_exc_info *exc_a = (struct ada_exc_info *) a;
const struct ada_exc_info *exc_b = (struct ada_exc_info *) b;
int result;
result = strcmp (exc_a->name, exc_b->name);
if (result != 0)
return result;
if (exc_a->addr < exc_b->addr)
return -1;
if (exc_a->addr > exc_b->addr)
return 1;
return 0;
}
/* Sort EXCEPTIONS using compare_ada_exception_info as the comparison
routine, but keeping the first SKIP elements untouched.
All duplicates are also removed. */
static void
sort_remove_dups_ada_exceptions_list (VEC(ada_exc_info) **exceptions,
int skip)
{
struct ada_exc_info *to_sort
= VEC_address (ada_exc_info, *exceptions) + skip;
int to_sort_len
= VEC_length (ada_exc_info, *exceptions) - skip;
int i, j;
qsort (to_sort, to_sort_len, sizeof (struct ada_exc_info),
compare_ada_exception_info);
for (i = 1, j = 1; i < to_sort_len; i++)
if (compare_ada_exception_info (&to_sort[i], &to_sort[j - 1]) != 0)
to_sort[j++] = to_sort[i];
to_sort_len = j;
VEC_truncate(ada_exc_info, *exceptions, skip + to_sort_len);
}
/* A function intended as the "name_matcher" callback in the struct
quick_symbol_functions' expand_symtabs_matching method.
SEARCH_NAME is the symbol's search name.
If USER_DATA is not NULL, it is a pointer to a regext_t object
used to match the symbol (by natural name). Otherwise, when USER_DATA
is null, no filtering is performed, and all symbols are a positive
match. */
static int
ada_exc_search_name_matches (const char *search_name, void *user_data)
{
regex_t *preg = user_data;
if (preg == NULL)
return 1;
/* In Ada, the symbol "search name" is a linkage name, whereas
the regular expression used to do the matching refers to
the natural name. So match against the decoded name. */
return (regexec (preg, ada_decode (search_name), 0, NULL, 0) == 0);
}
/* Add all exceptions defined by the Ada standard whose name match
a regular expression.
If PREG is not NULL, then this regexp_t object is used to
perform the symbol name matching. Otherwise, no name-based
filtering is performed.
EXCEPTIONS is a vector of exceptions to which matching exceptions
gets pushed. */
static void
ada_add_standard_exceptions (regex_t *preg, VEC(ada_exc_info) **exceptions)
{
int i;
for (i = 0; i < ARRAY_SIZE (standard_exc); i++)
{
if (preg == NULL
|| regexec (preg, standard_exc[i], 0, NULL, 0) == 0)
{
struct bound_minimal_symbol msymbol
= ada_lookup_simple_minsym (standard_exc[i]);
if (msymbol.minsym != NULL)
{
struct ada_exc_info info
= {standard_exc[i], SYMBOL_VALUE_ADDRESS (msymbol.minsym)};
VEC_safe_push (ada_exc_info, *exceptions, &info);
}
}
}
}
/* Add all Ada exceptions defined locally and accessible from the given
FRAME.
If PREG is not NULL, then this regexp_t object is used to
perform the symbol name matching. Otherwise, no name-based
filtering is performed.
EXCEPTIONS is a vector of exceptions to which matching exceptions
gets pushed. */
static void
ada_add_exceptions_from_frame (regex_t *preg, struct frame_info *frame,
VEC(ada_exc_info) **exceptions)
{
struct block *block = get_frame_block (frame, 0);
while (block != 0)
{
struct block_iterator iter;
struct symbol *sym;
ALL_BLOCK_SYMBOLS (block, iter, sym)
{
switch (SYMBOL_CLASS (sym))
{
case LOC_TYPEDEF:
case LOC_BLOCK:
case LOC_CONST:
break;
default:
if (ada_is_exception_sym (sym))
{
struct ada_exc_info info = {SYMBOL_PRINT_NAME (sym),
SYMBOL_VALUE_ADDRESS (sym)};
VEC_safe_push (ada_exc_info, *exceptions, &info);
}
}
}
if (BLOCK_FUNCTION (block) != NULL)
break;
block = BLOCK_SUPERBLOCK (block);
}
}
/* Add all exceptions defined globally whose name name match
a regular expression, excluding standard exceptions.
The reason we exclude standard exceptions is that they need
to be handled separately: Standard exceptions are defined inside
a runtime unit which is normally not compiled with debugging info,
and thus usually do not show up in our symbol search. However,
if the unit was in fact built with debugging info, we need to
exclude them because they would duplicate the entry we found
during the special loop that specifically searches for those
standard exceptions.
If PREG is not NULL, then this regexp_t object is used to
perform the symbol name matching. Otherwise, no name-based
filtering is performed.
EXCEPTIONS is a vector of exceptions to which matching exceptions
gets pushed. */
static void
ada_add_global_exceptions (regex_t *preg, VEC(ada_exc_info) **exceptions)
{
struct objfile *objfile;
struct symtab *s;
ALL_OBJFILES (objfile)
if (objfile->sf)
objfile->sf->qf->expand_symtabs_matching
(objfile, NULL, ada_exc_search_name_matches,
VARIABLES_DOMAIN, preg);
ALL_PRIMARY_SYMTABS (objfile, s)
{
struct blockvector *bv = BLOCKVECTOR (s);
int i;
for (i = GLOBAL_BLOCK; i <= STATIC_BLOCK; i++)
{
struct block *b = BLOCKVECTOR_BLOCK (bv, i);
struct block_iterator iter;
struct symbol *sym;
ALL_BLOCK_SYMBOLS (b, iter, sym)
if (ada_is_non_standard_exception_sym (sym)
&& (preg == NULL
|| regexec (preg, SYMBOL_NATURAL_NAME (sym),
0, NULL, 0) == 0))
{
struct ada_exc_info info
= {SYMBOL_PRINT_NAME (sym), SYMBOL_VALUE_ADDRESS (sym)};
VEC_safe_push (ada_exc_info, *exceptions, &info);
}
}
}
}
/* Implements ada_exceptions_list with the regular expression passed
as a regex_t, rather than a string.
If not NULL, PREG is used to filter out exceptions whose names
do not match. Otherwise, all exceptions are listed. */
static VEC(ada_exc_info) *
ada_exceptions_list_1 (regex_t *preg)
{
VEC(ada_exc_info) *result = NULL;
struct cleanup *old_chain
= make_cleanup (VEC_cleanup (ada_exc_info), &result);
int prev_len;
/* First, list the known standard exceptions. These exceptions
need to be handled separately, as they are usually defined in
runtime units that have been compiled without debugging info. */
ada_add_standard_exceptions (preg, &result);
/* Next, find all exceptions whose scope is local and accessible
from the currently selected frame. */
if (has_stack_frames ())
{
prev_len = VEC_length (ada_exc_info, result);
ada_add_exceptions_from_frame (preg, get_selected_frame (NULL),
&result);
if (VEC_length (ada_exc_info, result) > prev_len)
sort_remove_dups_ada_exceptions_list (&result, prev_len);
}
/* Add all exceptions whose scope is global. */
prev_len = VEC_length (ada_exc_info, result);
ada_add_global_exceptions (preg, &result);
if (VEC_length (ada_exc_info, result) > prev_len)
sort_remove_dups_ada_exceptions_list (&result, prev_len);
discard_cleanups (old_chain);
return result;
}
/* Return a vector of ada_exc_info.
If REGEXP is NULL, all exceptions are included in the result.
Otherwise, it should contain a valid regular expression,
and only the exceptions whose names match that regular expression
are included in the result.
The exceptions are sorted in the following order:
- Standard exceptions (defined by the Ada language), in
alphabetical order;
- Exceptions only visible from the current frame, in
alphabetical order;
- Exceptions whose scope is global, in alphabetical order. */
VEC(ada_exc_info) *
ada_exceptions_list (const char *regexp)
{
VEC(ada_exc_info) *result = NULL;
struct cleanup *old_chain = NULL;
regex_t reg;
if (regexp != NULL)
old_chain = compile_rx_or_error (&reg, regexp,
_("invalid regular expression"));
result = ada_exceptions_list_1 (regexp != NULL ? &reg : NULL);
if (old_chain != NULL)
do_cleanups (old_chain);
return result;
}
/* Implement the "info exceptions" command. */
static void
info_exceptions_command (char *regexp, int from_tty)
{
VEC(ada_exc_info) *exceptions;
struct cleanup *cleanup;
struct gdbarch *gdbarch = get_current_arch ();
int ix;
struct ada_exc_info *info;
exceptions = ada_exceptions_list (regexp);
cleanup = make_cleanup (VEC_cleanup (ada_exc_info), &exceptions);
if (regexp != NULL)
printf_filtered
(_("All Ada exceptions matching regular expression \"%s\":\n"), regexp);
else
printf_filtered (_("All defined Ada exceptions:\n"));
for (ix = 0; VEC_iterate(ada_exc_info, exceptions, ix, info); ix++)
printf_filtered ("%s: %s\n", info->name, paddress (gdbarch, info->addr));
do_cleanups (cleanup);
}
/* Operators */
/* Information about operators given special treatment in functions
below. */
@ -12956,6 +13307,12 @@ With an argument, catch only exceptions with the given name."),
varsize_limit = 65536;
add_info ("exceptions", info_exceptions_command,
_("\
List all Ada exception names.\n\
If a regular expression is passed as an argument, only those matching\n\
the regular expression are listed."));
obstack_init (&symbol_list_obstack);
decoded_names_store = htab_create_alloc

View File

@ -27,6 +27,7 @@ struct type_print_options;
#include "value.h"
#include "gdbtypes.h"
#include "breakpoint.h"
#include "vec.h"
/* Names of specific files known to be part of the runtime
system and that might consider (confusing) debugging information.
@ -389,6 +390,21 @@ extern void create_ada_exception_catchpoint
char *excep_string, char *cond_string, int tempflag, int disabled,
int from_tty);
/* Some information about a given Ada exception. */
typedef struct ada_exc_info
{
/* The name of the exception. */
const char *name;
/* The address of the symbol corresponding to that exception. */
CORE_ADDR addr;
} ada_exc_info;
DEF_VEC_O(ada_exc_info);
extern VEC(ada_exc_info) *ada_exceptions_list (const char *regexp);
/* Tasking-related: ada-tasks.c */
extern int valid_task_id (int);

View File

@ -1,3 +1,7 @@
2013-11-12 Joel Brobecker <brobecker@adacore.com>
* gdb.ada/info_exc: New testcase.
2013-11-11 Doug Evans <dje@google.com>
* gdb.arch/arm-bl-branch-dest.exp: Use gdb_test_file_name instead

View File

@ -0,0 +1,57 @@
# Copyright 2013 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/>.
load_lib "ada.exp"
standard_ada_testfile foo
if {[gdb_compile_ada "${srcfile}" "${binfile}" executable [list debug]] != "" } {
return -1
}
# A convenience function that joins all the arguments together,
# with a regexp that matches zero-or-more end of lines in between
# each argument. This function is ideal to write the expected output
# of a GDB command that generates more than a couple of lines, as
# this allows us to write each line as a separate string, which is
# easier to read by a human being.
proc multi_line { args } {
return [join $args "\[\r\n\]*"]
}
clean_restart ${testfile}
gdb_test "info exceptions" \
[multi_line "All defined Ada exceptions:" \
"constraint_error: $hex" \
"program_error: $hex" \
"storage_error: $hex" \
"tasking_error: $hex" \
"const.aint_global_e: $hex"]
gdb_test "info exceptions task" \
[multi_line "All Ada exceptions matching regular expression \"task\":" \
"tasking_error: $hex"]
gdb_test "info exceptions global" \
[multi_line "All Ada exceptions matching regular expression \"global\":" \
"const.aint_global_e: $hex"]
gdb_test "info exceptions const.aint" \
[multi_line "All Ada exceptions matching regular expression \"const\\.aint\":" \
"constraint_error: $hex" \
"const.aint_global_e: $hex"]

View File

@ -0,0 +1,18 @@
-- Copyright 2013 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/>.
package Const is
Aint_Global_E : exception;
end Const;

View File

@ -0,0 +1,20 @@
-- Copyright 2013 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/>.
with Const; use Const;
procedure Foo is
begin
raise Aint_Global_E;
end Foo;