| /* Everything about signal catchpoints, for GDB. |
| |
| Copyright (C) 2011-2021 Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| 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/>. */ |
| |
| #include "defs.h" |
| #include "arch-utils.h" |
| #include <ctype.h> |
| #include "breakpoint.h" |
| #include "gdbcmd.h" |
| #include "inferior.h" |
| #include "infrun.h" |
| #include "annotate.h" |
| #include "valprint.h" |
| #include "cli/cli-utils.h" |
| #include "completer.h" |
| #include "cli/cli-style.h" |
| #include "cli/cli-decode.h" |
| |
| #include <string> |
| |
| #define INTERNAL_SIGNAL(x) ((x) == GDB_SIGNAL_TRAP || (x) == GDB_SIGNAL_INT) |
| |
| /* An instance of this type is used to represent a signal catchpoint. |
| A breakpoint is really of this type iff its ops pointer points to |
| SIGNAL_CATCHPOINT_OPS. */ |
| |
| struct signal_catchpoint : public breakpoint |
| { |
| /* Signal numbers used for the 'catch signal' feature. If no signal |
| has been specified for filtering, it is empty. Otherwise, |
| it holds a list of all signals to be caught. */ |
| |
| std::vector<gdb_signal> signals_to_be_caught; |
| |
| /* If SIGNALS_TO_BE_CAUGHT is empty, then all "ordinary" signals are |
| caught. If CATCH_ALL is true, then internal signals are caught |
| as well. If SIGNALS_TO_BE_CAUGHT is not empty, then this field |
| is ignored. */ |
| |
| bool catch_all; |
| }; |
| |
| /* The breakpoint_ops structure to be used in signal catchpoints. */ |
| |
| static struct breakpoint_ops signal_catchpoint_ops; |
| |
| /* Count of each signal. */ |
| |
| static unsigned int signal_catch_counts[GDB_SIGNAL_LAST]; |
| |
| |
| |
| /* A convenience wrapper for gdb_signal_to_name that returns the |
| integer value if the name is not known. */ |
| |
| static const char * |
| signal_to_name_or_int (enum gdb_signal sig) |
| { |
| const char *result = gdb_signal_to_name (sig); |
| |
| if (strcmp (result, "?") == 0) |
| result = plongest (sig); |
| |
| return result; |
| } |
| |
| |
| |
| /* Implement the "insert_location" breakpoint_ops method for signal |
| catchpoints. */ |
| |
| static int |
| signal_catchpoint_insert_location (struct bp_location *bl) |
| { |
| struct signal_catchpoint *c = (struct signal_catchpoint *) bl->owner; |
| |
| if (!c->signals_to_be_caught.empty ()) |
| { |
| for (gdb_signal iter : c->signals_to_be_caught) |
| ++signal_catch_counts[iter]; |
| } |
| else |
| { |
| for (int i = 0; i < GDB_SIGNAL_LAST; ++i) |
| { |
| if (c->catch_all || !INTERNAL_SIGNAL (i)) |
| ++signal_catch_counts[i]; |
| } |
| } |
| |
| signal_catch_update (signal_catch_counts); |
| |
| return 0; |
| } |
| |
| /* Implement the "remove_location" breakpoint_ops method for signal |
| catchpoints. */ |
| |
| static int |
| signal_catchpoint_remove_location (struct bp_location *bl, |
| enum remove_bp_reason reason) |
| { |
| struct signal_catchpoint *c = (struct signal_catchpoint *) bl->owner; |
| |
| if (!c->signals_to_be_caught.empty ()) |
| { |
| for (gdb_signal iter : c->signals_to_be_caught) |
| { |
| gdb_assert (signal_catch_counts[iter] > 0); |
| --signal_catch_counts[iter]; |
| } |
| } |
| else |
| { |
| for (int i = 0; i < GDB_SIGNAL_LAST; ++i) |
| { |
| if (c->catch_all || !INTERNAL_SIGNAL (i)) |
| { |
| gdb_assert (signal_catch_counts[i] > 0); |
| --signal_catch_counts[i]; |
| } |
| } |
| } |
| |
| signal_catch_update (signal_catch_counts); |
| |
| return 0; |
| } |
| |
| /* Implement the "breakpoint_hit" breakpoint_ops method for signal |
| catchpoints. */ |
| |
| static int |
| signal_catchpoint_breakpoint_hit (const struct bp_location *bl, |
| const address_space *aspace, |
| CORE_ADDR bp_addr, |
| const struct target_waitstatus *ws) |
| { |
| const struct signal_catchpoint *c |
| = (const struct signal_catchpoint *) bl->owner; |
| gdb_signal signal_number; |
| |
| if (ws->kind != TARGET_WAITKIND_STOPPED) |
| return 0; |
| |
| signal_number = ws->value.sig; |
| |
| /* If we are catching specific signals in this breakpoint, then we |
| must guarantee that the called signal is the same signal we are |
| catching. */ |
| if (!c->signals_to_be_caught.empty ()) |
| { |
| for (gdb_signal iter : c->signals_to_be_caught) |
| if (signal_number == iter) |
| return 1; |
| /* Not the same. */ |
| return 0; |
| } |
| else |
| return c->catch_all || !INTERNAL_SIGNAL (signal_number); |
| } |
| |
| /* Implement the "print_it" breakpoint_ops method for signal |
| catchpoints. */ |
| |
| static enum print_stop_action |
| signal_catchpoint_print_it (bpstat bs) |
| { |
| struct breakpoint *b = bs->breakpoint_at; |
| struct target_waitstatus last; |
| const char *signal_name; |
| struct ui_out *uiout = current_uiout; |
| |
| get_last_target_status (nullptr, nullptr, &last); |
| |
| signal_name = signal_to_name_or_int (last.value.sig); |
| |
| annotate_catchpoint (b->number); |
| maybe_print_thread_hit_breakpoint (uiout); |
| |
| printf_filtered (_("Catchpoint %d (signal %s), "), b->number, signal_name); |
| |
| return PRINT_SRC_AND_LOC; |
| } |
| |
| /* Implement the "print_one" breakpoint_ops method for signal |
| catchpoints. */ |
| |
| static void |
| signal_catchpoint_print_one (struct breakpoint *b, |
| struct bp_location **last_loc) |
| { |
| struct signal_catchpoint *c = (struct signal_catchpoint *) b; |
| struct value_print_options opts; |
| struct ui_out *uiout = current_uiout; |
| |
| get_user_print_options (&opts); |
| |
| /* Field 4, the address, is omitted (which makes the columns |
| not line up too nicely with the headers, but the effect |
| is relatively readable). */ |
| if (opts.addressprint) |
| uiout->field_skip ("addr"); |
| annotate_field (5); |
| |
| if (c->signals_to_be_caught.size () > 1) |
| uiout->text ("signals \""); |
| else |
| uiout->text ("signal \""); |
| |
| if (!c->signals_to_be_caught.empty ()) |
| { |
| std::string text; |
| |
| bool first = true; |
| for (gdb_signal iter : c->signals_to_be_caught) |
| { |
| const char *name = signal_to_name_or_int (iter); |
| |
| if (!first) |
| text += " "; |
| first = false; |
| |
| text += name; |
| } |
| uiout->field_string ("what", text); |
| } |
| else |
| uiout->field_string ("what", |
| c->catch_all ? "<any signal>" : "<standard signals>", |
| metadata_style.style ()); |
| uiout->text ("\" "); |
| |
| if (uiout->is_mi_like_p ()) |
| uiout->field_string ("catch-type", "signal"); |
| } |
| |
| /* Implement the "print_mention" breakpoint_ops method for signal |
| catchpoints. */ |
| |
| static void |
| signal_catchpoint_print_mention (struct breakpoint *b) |
| { |
| struct signal_catchpoint *c = (struct signal_catchpoint *) b; |
| |
| if (!c->signals_to_be_caught.empty ()) |
| { |
| if (c->signals_to_be_caught.size () > 1) |
| printf_filtered (_("Catchpoint %d (signals"), b->number); |
| else |
| printf_filtered (_("Catchpoint %d (signal"), b->number); |
| |
| for (gdb_signal iter : c->signals_to_be_caught) |
| { |
| const char *name = signal_to_name_or_int (iter); |
| |
| printf_filtered (" %s", name); |
| } |
| printf_filtered (")"); |
| } |
| else if (c->catch_all) |
| printf_filtered (_("Catchpoint %d (any signal)"), b->number); |
| else |
| printf_filtered (_("Catchpoint %d (standard signals)"), b->number); |
| } |
| |
| /* Implement the "print_recreate" breakpoint_ops method for signal |
| catchpoints. */ |
| |
| static void |
| signal_catchpoint_print_recreate (struct breakpoint *b, struct ui_file *fp) |
| { |
| struct signal_catchpoint *c = (struct signal_catchpoint *) b; |
| |
| fprintf_unfiltered (fp, "catch signal"); |
| |
| if (!c->signals_to_be_caught.empty ()) |
| { |
| for (gdb_signal iter : c->signals_to_be_caught) |
| fprintf_unfiltered (fp, " %s", signal_to_name_or_int (iter)); |
| } |
| else if (c->catch_all) |
| fprintf_unfiltered (fp, " all"); |
| fputc_unfiltered ('\n', fp); |
| } |
| |
| /* Implement the "explains_signal" breakpoint_ops method for signal |
| catchpoints. */ |
| |
| static int |
| signal_catchpoint_explains_signal (struct breakpoint *b, enum gdb_signal sig) |
| { |
| return 1; |
| } |
| |
| /* Create a new signal catchpoint. TEMPFLAG is true if this should be |
| a temporary catchpoint. FILTER is the list of signals to catch; it |
| can be empty, meaning all signals. CATCH_ALL is a flag indicating |
| whether signals used internally by gdb should be caught; it is only |
| valid if FILTER is NULL. If FILTER is empty and CATCH_ALL is zero, |
| then internal signals like SIGTRAP are not caught. */ |
| |
| static void |
| create_signal_catchpoint (int tempflag, std::vector<gdb_signal> &&filter, |
| bool catch_all) |
| { |
| struct gdbarch *gdbarch = get_current_arch (); |
| |
| std::unique_ptr<signal_catchpoint> c (new signal_catchpoint ()); |
| init_catchpoint (c.get (), gdbarch, tempflag, NULL, &signal_catchpoint_ops); |
| c->signals_to_be_caught = std::move (filter); |
| c->catch_all = catch_all; |
| |
| install_breakpoint (0, std::move (c), 1); |
| } |
| |
| |
| /* Splits the argument using space as delimiter. Returns a filter |
| list, which is empty if no filtering is required. */ |
| |
| static std::vector<gdb_signal> |
| catch_signal_split_args (const char *arg, bool *catch_all) |
| { |
| std::vector<gdb_signal> result; |
| bool first = true; |
| |
| while (*arg != '\0') |
| { |
| int num; |
| gdb_signal signal_number; |
| char *endptr; |
| |
| std::string one_arg = extract_arg (&arg); |
| if (one_arg.empty ()) |
| break; |
| |
| /* Check for the special flag "all". */ |
| if (one_arg == "all") |
| { |
| arg = skip_spaces (arg); |
| if (*arg != '\0' || !first) |
| error (_("'all' cannot be caught with other signals")); |
| *catch_all = true; |
| gdb_assert (result.empty ()); |
| return result; |
| } |
| |
| first = false; |
| |
| /* Check if the user provided a signal name or a number. */ |
| num = (int) strtol (one_arg.c_str (), &endptr, 0); |
| if (*endptr == '\0') |
| signal_number = gdb_signal_from_command (num); |
| else |
| { |
| signal_number = gdb_signal_from_name (one_arg.c_str ()); |
| if (signal_number == GDB_SIGNAL_UNKNOWN) |
| error (_("Unknown signal name '%s'."), one_arg.c_str ()); |
| } |
| |
| result.push_back (signal_number); |
| } |
| |
| result.shrink_to_fit (); |
| return result; |
| } |
| |
| /* Implement the "catch signal" command. */ |
| |
| static void |
| catch_signal_command (const char *arg, int from_tty, |
| struct cmd_list_element *command) |
| { |
| int tempflag; |
| bool catch_all = false; |
| std::vector<gdb_signal> filter; |
| |
| tempflag = command->context () == CATCH_TEMPORARY; |
| |
| arg = skip_spaces (arg); |
| |
| /* The allowed syntax is: |
| catch signal |
| catch signal <name | number> [<name | number> ... <name | number>] |
| |
| Let's check if there's a signal name. */ |
| |
| if (arg != NULL) |
| filter = catch_signal_split_args (arg, &catch_all); |
| |
| create_signal_catchpoint (tempflag, std::move (filter), catch_all); |
| } |
| |
| static void |
| initialize_signal_catchpoint_ops (void) |
| { |
| struct breakpoint_ops *ops; |
| |
| initialize_breakpoint_ops (); |
| |
| ops = &signal_catchpoint_ops; |
| *ops = base_breakpoint_ops; |
| ops->insert_location = signal_catchpoint_insert_location; |
| ops->remove_location = signal_catchpoint_remove_location; |
| ops->breakpoint_hit = signal_catchpoint_breakpoint_hit; |
| ops->print_it = signal_catchpoint_print_it; |
| ops->print_one = signal_catchpoint_print_one; |
| ops->print_mention = signal_catchpoint_print_mention; |
| ops->print_recreate = signal_catchpoint_print_recreate; |
| ops->explains_signal = signal_catchpoint_explains_signal; |
| } |
| |
| void _initialize_break_catch_sig (); |
| void |
| _initialize_break_catch_sig () |
| { |
| initialize_signal_catchpoint_ops (); |
| |
| add_catch_command ("signal", _("\ |
| Catch signals by their names and/or numbers.\n\ |
| Usage: catch signal [[NAME|NUMBER] [NAME|NUMBER]...|all]\n\ |
| Arguments say which signals to catch. If no arguments\n\ |
| are given, every \"normal\" signal will be caught.\n\ |
| The argument \"all\" means to also catch signals used by GDB.\n\ |
| Arguments, if given, should be one or more signal names\n\ |
| (if your system supports that), or signal numbers."), |
| catch_signal_command, |
| signal_completer, |
| CATCH_PERMANENT, |
| CATCH_TEMPORARY); |
| } |